package XML::MinWriter; $XML::MinWriter::VERSION = '0.08'; use strict; use warnings; use Carp; require Exporter; use XML::Writer; our @ISA = qw(Exporter XML::Writer); our @EXPORT_OK = qw(); our @EXPORT = qw(); sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self->{PYX_TYPE} = ''; $self->{PYX_TAG} = ''; $self->{PYX_ATTR} = []; bless $self, $class; # reconsecrate } sub xmlDecl { my $self = shift; $self->flush_pyx; $self->SUPER::xmlDecl(@_); } sub doctype { my $self = shift; $self->flush_pyx; $self->SUPER::doctype(@_); } sub comment { my $self = shift; $self->flush_pyx; $self->SUPER::comment(@_); } sub pi { my $self = shift; $self->flush_pyx; $self->SUPER::pi(@_); } sub startTag { my $self = shift; $self->flush_pyx; $self->SUPER::startTag(@_); } sub emptyTag { my $self = shift; $self->flush_pyx; $self->SUPER::emptyTag(@_); } sub endTag { my $self = shift; $self->flush_pyx; $self->SUPER::endTag(@_); } sub characters { my $self = shift; $self->flush_pyx; $self->SUPER::characters(@_); } sub raw { my $self = shift; $self->flush_pyx; $self->SUPER::raw(@_); } sub cdata { my $self = shift; $self->flush_pyx; $self->SUPER::cdata(@_); } sub dataElement { my $self = shift; $self->flush_pyx; $self->SUPER::dataElement(@_); } sub end { my $self = shift; $self->flush_pyx; $self->SUPER::end(@_); } sub write_pyx { my $self = shift; my @inlist; for (@_) { push @inlist, split m{\n}xms; } LOOP1: for my $instr (@inlist) { if ($instr eq '') { next LOOP1; } my $code = substr($instr, 0, 1); my $text = substr($instr, 1); $text =~ s{\\(.)}{ $1 eq '\\' ? "\\" : $1 eq 'n' ? "\n" : $1 eq 't' ? "\t" : "\\".$1}xmsge; if ($code eq '(') { $self->flush_pyx; $self->{PYX_TYPE} = '('; $self->{PYX_TAG} = $text; $self->{PYX_ATTR} = []; } elsif ($code eq 'A') { my ($key, $val) = $text =~ m{\A (\S+) \s+ (.*) \z}xms; unless (defined($key) and defined($val)) { carp "Can't parse (key, val) [code = 'A'] in '$text' in write_pyx()"; next LOOP1; } push @{$self->{PYX_ATTR}}, $key, $val; } elsif ($code eq '?') { my ($intro, $def) = $text =~ m{\A (\S+) \s+ (.*) \z}xms; unless (defined($intro) and defined($def)) { carp "Can't parse (intro, def) [code = '?'] in '$text' in write_pyx()"; next LOOP1; } if ($intro =~ m{\A xml}xmsi and $intro !~ m{\A xml-stylesheet \z}xmsi) { my ($version, $encoding, $standalone); my $data = $def; while (my ($key, $val, $rest) = $data =~ m{\A (\S+) \s* = \s* ["']([^"']+)["'] \s* (.*) \z}xms) { if ($key =~ m{\A version \z}xmsi) { $version = $val; } elsif ($key =~ m{\A encoding \z}xmsi) { $encoding = $val; } elsif ($key =~ m{\A standalone \z}xmsi) { $standalone = $val; } else { carp "Found invalid XML-Declaration (key = '$key') in (intro = '$intro', def = '$def') in write_pyx()"; next LOOP1; } unless (defined $version) { $version = '1.0'; } unless ($version eq '1.0') { carp "Found version other than 1.0 ('$version') in (intro = '$intro', def = '$def') in write_pyx()"; next LOOP1; } $data = $rest; } $self->xmlDecl($encoding, $standalone); } else { $self->pi($intro, $def); } } elsif ($code eq '!') { my ($intro, $def) = $text =~ m{\A (\S+) \s+ (.*) \z}xms; unless (defined($intro) and defined($def)) { carp "Can't parse (intro, def) [code = '!'] in '$text' in write_pyx()"; next LOOP1; } if ($def =~ m{\A PUBLIC}xmsi) { my ($public, $system) = $def =~ m{\A PUBLIC \s+ ["']([^"']+)["'] \s+ ["']([^"']+)["'] \s* \z}xmsi; unless (defined($public) and defined($system)) { carp "Can't parse DOCTYPE PUBLIC in (intro = '$intro', def = '$def') in write_pyx()"; next LOOP1; } $self->doctype($intro, $public, $system); } elsif ($def =~ m{\A SYSTEM}xmsi) { my ($system) = $def =~ m{\A SYSTEM \s+ ["']([^"']+)["'] \s* \z}xmsi; unless (defined($system)) { carp "Can't parse DOCTYPE SYSTEM in (intro = '$intro', def = '$def') in write_pyx()"; next LOOP1; } $self->doctype($intro, undef, $system); } else { carp "Can't find neither PUBLIC nor SYSTEM in DOCTYPE (intro = '$intro', def = '$def') in write_pyx()"; next LOOP1; } } elsif ($code eq ')') { $self->endTag($text); } elsif ($code eq '-') { $self->characters($text); } elsif ($code eq '#') { $self->comment($text); } else { carp "Invalid code = '$code' in write_pyx()"; next LOOP1; } } } sub flush_pyx { my $self = shift; if ($self->{PYX_TYPE} eq '(') { $self->SUPER::startTag($self->{PYX_TAG}, @{$self->{PYX_ATTR}}); } $self->{PYX_TYPE} = ''; $self->{PYX_TAG} = ''; $self->{PYX_ATTR} = []; } 1; __END__ =head1 NAME XML::MinWriter - Perl extension for writing XML in PYX format. =head1 SYNOPSIS Here is a simple example of how to use XML::MinWriter: use XML::MinWriter; open my $fh, '>', \my $xml or die $!; my $wrt = XML::MinWriter->new(OUTPUT => $fh, DATA_MODE => 1, DATA_INDENT => 2); $wrt->xmlDecl('iso-8859-1'); $wrt->startTag('alpha'); $wrt->startTag('beta', p1 => 'dat1', p2 => 'dat2'); $wrt->characters('abcdefg'); $wrt->endTag('beta'); $wrt->write_pyx('(gamma'); $wrt->write_pyx('-hijklmn'); $wrt->write_pyx(')gamma'); $wrt->endTag('alpha'); $wrt->end; close $fh; print "The XML generated is as follows:\n\n"; print $xml, "\n"; ...and this is the output: abcdefg hijklmn =head1 DESCRIPTION =head2 Introduction XML::MinWriter is a module to write XML in PYX Format. It inherits from XML::Writer and adds a new method C. Modules XML::TiePYX and XML::Reader produce PYX which can then be fed into XML::MinWriter to generate XML using the write_pyx() method. =head2 Pyx Pyx is a line-oriented text format to represent XML. The first character of a line in Pyx represents the type. This first character type can be: '(' => a Start tag, '(item' translates into '' ')' => an End tag, ')item' translates into '' '-' => Character data, '-data' translates into 'data' 'A' => Attributes, 'Aattr v1' translates into '<... attr="v1">' '?' => Process Instructions, '?xml dat="p1"' translates into '' '#' => Comments, '#remark' translates into '' '!' => Doctype, '!tag SYSTEM "abc"' translates into '' =head2 Example using Pyx For example the following PYX code: use XML::MinWriter; open my $fh, '>', \my $xml or die $!; my $wrt = XML::MinWriter->new(OUTPUT => $fh, DATA_MODE => 1, DATA_INDENT => 2); $wrt->write_pyx('?xml version="1.0" encoding="iso-8859-1"'); $wrt->write_pyx('(data'); $wrt->write_pyx('(item'); $wrt->write_pyx('Aattr1 p1'); $wrt->write_pyx('Aattr2 p2'); $wrt->write_pyx('-line'); $wrt->write_pyx(')item'); $wrt->write_pyx('(level'); $wrt->write_pyx('#remark'); $wrt->write_pyx(')level'); $wrt->write_pyx(')data'); $wrt->end; close $fh; print "The XML generated is as follows:\n\n"; print $xml, "\n"; ...generates the following XML: line =head2 Example using XML::Reader A sample code fragment that uses XML::Reader together with XML::MinWriter: use XML::Reader; use XML::MinWriter; my $line = q{ one two iuertyieruyt three four }; my $rdr = XML::Reader->new(\$line, { using => '/data/order/database/customer', mode => 'pyx', }); open my $fh, '>', \my $xml or die "Error-0010: Can't open > xml because $!"; my $wrt = XML::MinWriter->new(OUTPUT => $fh, DATA_MODE => 1, DATA_INDENT => 2); $wrt->xmlDecl('iso-8859-1'); $wrt->doctype('delta', 'public', 'system'); $wrt->startTag('delta'); while ($rdr->iterate) { $wrt->write_pyx($rdr->pyx); } $wrt->endTag('delta'); $wrt->end; close $fh; print $xml, "\n"; This is the resulting XML: one two three four =head1 AUTHOR Klaus Eichner, October 2011 =head1 COPYRIGHT AND LICENSE Copyright (C) 2011 by Klaus Eichner XML::MinWriter is free software; you can redistribute and/or modify XML::MinWriter under the same terms as Perl itself. =head1 SEE ALSO L, L, L. =cut