package XML::Struct::Writer::Stream; use strict; use Moo; our $VERSION = '0.27'; has fh => (is => 'rw', default => sub { *STDOUT }); has pretty => (is => 'rw'); our %ESCAPE = ( '&' => '&', '<' => '<', '>' => '>', '"' => '"', ); use constant { DOCUMENT_STARTED => 0, TAG_STARTED => 1, CHAR_CONTENT => 2, CHILD_ELEMENT => 3, }; sub xml_decl { my ($self, $data) = @_; my $xml = "{Version}\""; $xml .= " encoding=\"$data->{Encoding}\"" if $data->{Encoding}; $xml .= " standalone=\"$data->{Standalone}\"" if $data->{Standalone}; $xml .= "?>\n"; print {$self->fh} $xml; } sub start_document { my ($self) = @_; $self->{_stack} = []; $self->{_status} = DOCUMENT_STARTED; } sub start_element { my ($self, $data) = @_; my $tag = $data->{Name}; my $attr = $data->{Attributes}; my $xml = "<$tag"; my $status = $self->{_status} // DOCUMENT_STARTED; if ($status == TAG_STARTED) { print {$self->fh} '>'; if ($self->pretty) { print {$self->fh} "\n".(' ' x (scalar @{$self->{_stack}})); } } elsif ($status == CHILD_ELEMENT) { if ($self->pretty) { print {$self->fh} "\n".(' ' x (scalar @{$self->{_stack}})); } } elsif ($status == CHAR_CONTENT) { print {$self->fh} $self->{_chars}; } # else: DOCUMENT_STARTED push @{$self->{_stack}}, $tag; if ($attr && %$attr) { foreach my $key (sort keys %$attr) { my $value = $attr->{$key}; $value =~ s/([&<>"])/$ESCAPE{$1}/geo; $xml .= " $key=\"$value\""; } } $self->{_status} = TAG_STARTED; print {$self->fh} $xml; } sub end_element { my ($self) = @_; my $tag = pop @{$self->{_stack}} or return; if ($self->{_status} == TAG_STARTED) { print {$self->fh} '/>'; } elsif ($self->{_status} == CHAR_CONTENT) { print {$self->fh} $self->{_chars} . ""; $self->{_chars} = ""; } else { # CHILD_ELEMENT if ($self->pretty) { print {$self->fh} "\n".(' ' x (scalar @{$self->{_stack}})); } print {$self->fh} ""; } $self->{_status} = CHILD_ELEMENT; } sub characters { my ($self, $data) = @_; my $xml = $data->{Data}; $xml =~ s/([&<>])/$ESCAPE{$1}/geo; if ($self->{_status} == TAG_STARTED) { print {$self->fh} '>'; $self->{_status} = CHAR_CONTENT; $self->{_chars} = $xml; } elsif ($self->{_status} == CHILD_ELEMENT) { print {$self->fh} $xml; } else { $self->{_chars} .= $xml; } } sub end_document { my ($self) = @_; $self->end_element while @{$self->{_stack}}; print {$self->fh} "\n"; } 1; __END__ =head1 NAME XML::Struct::Writer::Stream - simplified SAX handler to serialize (Micro)XML =head1 DESCRIPTION This class implements a simplfied SAX handler for stream-based serialization of XML. DTDs, comments, processing instructions and similar features not part of MicroXML are not supported. The handler is written to reproduce the serialization of libxml. =head1 CONFIGURATION =over =item fh File handle or compatible object to write to (standard output by default). =item pretty Pretty-print XML if enabled. =back =head1 SEE ALSO See L, L, and L for more elaborated SAX writers and L for a general XML writer, not based on SAX. =cut