YAWriter(3)

πŸ’… YAWriter(3) β€” XML::Handler::YAWriter, aka Yet Another Perl SAX XML Writer πŸŽ€

okay bestie, so this is a page from the User Contributed Perl Documentation section of the manual β€” and it's about <pink>XML::Handler::YAWriter</pink>, which is, say it with me, yet another Perl SAX XML Writer. iconic name, honestly. let's get into it ✨


🌸 NAME 🌸

XML::Handler::YAWriter β€” Yet another Perl SAX XML Writer πŸ’–


πŸ¦‹ SYNOPSIS πŸ¦‹

use XML::Handler::YAWriter;

my $ya = new XML::Handler::YAWriter( %options );
my $perlsax = new XML::Parser::PerlSAX( 'Handler' => $ya );

that's it. that's the whole vibe. you make a writer, you hand it to a parser, you go live πŸ’…


✨ DESCRIPTION ✨

okay so real talk: YAWriter implements Yet Another XML::Handler::Writer. why does this one exist? two reasons, and they're both extremely valid:

  • the author needed a flexible escaping technique πŸŽ€
  • and wanted some kind of pretty printing πŸ¦‹

if you create an instance of YAWriter with no options at all, the default behavior is to produce an array of strings containing the XML right here:

@{$ya->{Strings}}

no cap, that's the default. it just... puts the strings there. bestie behavior 🫢


🌈 Options 🌈

options are given in the usual 'key' => 'value' idiom, because of course they are πŸ’…

  • Output IO::File β€” this option tells YAWriter to use an already-open file for output, instead of using $ya->{Strings} to store the array of strings. and ngl the important part: the only thing the object needs to implement is the print method. so literally anything can be used to receive a stream of strings from YAWriter. anything. a file, a socket, your weird custom little guy. slay πŸ”₯
  • AsFile string β€” this option makes start_document open the named file and end_document close it. use the literal dash - if you want to print on standard output πŸ‘‘
  • AsPipe string β€” this makes start_document open a pipe and end_document close it. the pipe is a normal shell command. secure shell comes in handy here, but heads up: it has a 2GB limit on most systems πŸ’€
  • AsArray boolean β€” forces storage of the XML in $ya->{Strings}, even if the Output option is given. it's giving "i know you gave me a file but i'm putting it in the array anyway" πŸ™ˆ
  • AsString boolean β€” causes end_document to return the complete XML document in a single string. most SAX drivers return the value of end_document as a result of their parse method. however β€” and this is the tea β€” as this may not work with some combinations of SAX drivers and filters, a join of $ya->{Strings} in the controlling method is preferred πŸ‘€
  • Encoding string β€” changes the default encoding from UTF-8 to anything you like. you should make sure the given data are already in this encoding, or provide an Escape hash so YAWriter knows about the recoding 🧚
  • Escape hash β€” okay so this hash defines substitutions that have to be done to any string β€” with the exception of the processing_instruction and doctype_decl methods, where (in the author's opinion) escaping of target and data would cause more trouble than necessary. fair enough tbh.

the default value for Escape is:

$XML::Handler::YAWriter::escape = {
        '&'  => '&amp;',
        '<'  => '&lt;',
        '>'  => '&gt;',
        '"'  => '&quot;',
        '--' => '&#45;&#45;'
        };

YAWriter will use an evaluated sub to make the recoding based on a given Escape hash reasonably fast. future versions may use XS to improve this performance bottleneck. we love a noted bottleneck πŸ’…

  • Pretty hash β€” a hash of string => boolean tuples, to define the kind of prettyprinting. defaults to undef. possible string values are down below πŸ‘‡
    • AddHiddenNewline boolean β€” add hidden newline before ">"
    • AddHiddenAttrTab boolean β€” add hidden tabulation for attributes
    • CatchEmptyElement boolean β€” catch empty Elements, apply "/>" compression
    • CatchWhiteSpace boolean β€” catch whitespace with comments
    • CompactAttrIndent β€” places Attributes on the same line as the Element
    • IsSGML boolean β€” this option causes start_document, processing_instruction and doctype_decl to appear as SGML. the SGML is still well-formed of course, if your SAX events are well-formed. iykyk πŸ¦‹
    • NoComments boolean β€” supress Comments
    • NoDTD boolean β€” supress DTD
    • NoPI boolean β€” supress Processing Instructions
    • NoProlog boolean β€” supress <?xml ... ?> Prolog
    • NoWhiteSpace boolean β€” supress WhiteSpace to clean documents from prior pretty printing. we stan a cleanup queen 🧹
    • PrettyWhiteIndent boolean β€” add visible indent before any eventstring
    • PrettyWhiteNewline boolean β€” add visible newlines before any eventstring
    • SAX1 boolean (not yet implemented) β€” output only SAX1 compliant eventstrings. yeah. it's not implemented yet. don't come for me πŸ’€

🌸 Notes: 🌸

correct handling of start_document and end_document is required! like, required required. no skipping. ⚠️

the YAWriter Object initialises its structures during start_document and does its cleanup during end_document. if you forget to call start_document, any other method will break during the run. most likely place is the encode method, trying to eval undef as a subroutine. and if you forget to call end_document, you should not use a single instance of YAWriter more than once. that's the whole warning. it's serious 😭

for small documents AsArray may be the fastest method and AsString the easiest one to receive the output of YAWriter. but AsString and AsArray may run out of memory with infinite SAX streams. the only method XML::Handler::Writer calls on a given Output object is the print method. so it's easy to use a self-written Output object to improve streaming. go off, write your own little output guy 🫢

a single instance of XML::Handler::YAWriter is able to produce more than one file in a single run. be sure to provide a fresh IO::File as Output before you call start_document, and close this File after calling end_document. or provide a filename in AsFile, so start_document and end_document can open and close its own filehandle. either way, be responsible ✨

automatic recoding between 8bit and 16bit does not work in any Perl correctly ! yeah. that's the sentence. i'm just the messenger πŸ’€

the author has Perl-5.00563 at home and there they can specify use utf8; in the right places to make recoding work. but they dislike saying use 5.00555; because many systems run 5.00503. relatable struggle tbh 😭

if you use some 8bit character set internally and want to use national characters, either state your character as Encoding to be ISO-8859-1, or provide an Escape hash similar to the following:

$ya->{'Escape'} = {
                '&'  => '&amp;',
                '<'  => '&lt;',
                '>'  => '&gt;',
                '"'  => '&quot;',
                '--' => '&#45;&#45;'
                'ΓΆ' => '&ouml;'
                'Γ€' => '&auml;'
                'ΓΌ' => '&uuml;'
                'Γ–' => '&Ouml;'
                'Γ„' => '&Auml;'
                'Ü' => '&Uuml;'
                'ß' => '&szlig;'
                };

you may abuse YAWriter to clean whitespace from XML documents πŸ’… take a look at test.pl, doing just that with an XML::Edifact message, without querying the DTD. this may work in 99% of the cases where you want to get rid of ignorable whitespace caused by the various forms of pretty printing:

my $ya = new XML::Handler::YAWriter(
    'Output' => new IO::File ( ">-" );
    'Pretty' => {
        'NoWhiteSpace'=>1,
        'NoComments'=>1,
        'AddHiddenNewline'=>1,
        'AddHiddenAttrTab'=>1,
    } );

XML::Handler::Writer implements any method XML::Parser::PerlSAX wants. this extends the Java SAX1.0 specification πŸ”₯ the author has in mind using Pretty=>SAX1=>1 to disable this feature, if abusing YAWriter for a SAX proxy. future plans, bestie. visionary behavior πŸ‘‘


πŸŽ€ AUTHOR πŸŽ€

Michael Koehne, Kraehe@Copyleft.De πŸ’–


🫢 Thanks 🫢

"Derksen, Eduard (Enno), CSCIO" <enno@att.com> helped with the Escape hash and gave quite a lot of useful comments. we love an appreciative king πŸ‘‘βœ¨


⭐ SEE ALSO ⭐

perl and XML::Parser::PerlSAX β€” go read those next, bestie πŸ¦‹


πŸ’€ POD ERRORS πŸ’€

hey! the above document had some coding errors, which are explained below. not me being the one to tell you this πŸ™ˆ

Around line 465:

Non-ASCII character seen before =encoding in ''ΓΆ''. Assuming CP1252

and that's the whole page, bestie πŸ’… this was perl v5.42.0, dated 2013-04-22, and the page itself is YAWriter(3) β€” mwah, go write some well-formed XML, and remember: call start_document. and call end_document. or else. πŸŽ€βœ¨

πŸ“¦ where this page comes from