File Coverage

blib/lib/XML/Struct/Writer.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package XML::Struct::Writer;
2 2     2   31194 use strict;
  2         3  
  2         82  
3              
4 2     2   1017 use Moo;
  2         22848  
  2         10  
5 2     2   4262 use XML::LibXML::SAX::Builder;
  0            
  0            
6             use XML::Struct::Writer::Stream;
7             use Scalar::Util qw(blessed reftype);
8             use Carp;
9              
10             our $VERSION = '0.26';
11              
12             has attributes => (is => 'rw', default => sub { 1 });
13             has encoding => (is => 'rw', default => sub { 'UTF-8' });
14             has version => (is => 'rw', default => sub { '1.0' });
15             has standalone => (is => 'rw');
16             has pretty => (is => 'rw', default => sub { 0 }); # 0|1|2
17             has xmldecl => (is => 'rw', default => sub { 1 });
18             has handler => (is => 'lazy', builder => 1);
19              
20             has to => (
21             is => 'rw',
22             coerce => sub {
23             if (!ref $_[0]) {
24             return IO::File->new($_[0], 'w');
25             } elsif (reftype($_[0]) eq 'SCALAR') {
26             open my $io,">:utf8",$_[0];
27             return $io;
28             } else { # IO::Handle, GLOB, ...
29             return $_[0];
30             }
31             },
32             trigger => sub { delete $_[0]->{handler} }
33             );
34              
35             sub _build_handler {
36             $_[0]->to ? XML::Struct::Writer::Stream->new(
37             fh => $_[0]->to,
38             encoding => $_[0]->encoding,
39             version => $_[0]->version,
40             pretty => $_[0]->pretty,
41             ) : XML::LibXML::SAX::Builder->new( handler => $_[0] );
42             }
43              
44             sub write {
45             my ($self, $element, $name) = @_;
46              
47             $self->writeStart;
48             $self->writeElement(
49             $self->microXML($element, $name // 'root')
50             );
51             $self->writeEnd;
52            
53             $self->handler->can('result') ? $self->handler->result : 1;
54             }
55              
56             *writeDocument = \&write;
57              
58             # TODO: Make available as function in XML::Struct or XML::Struct::Simple
59             sub microXML {
60             my ($self, $element, $name) = @_;
61              
62             my $type = reftype($element);
63             if ($type) {
64             # MicroXML
65             if ($type eq 'ARRAY') {
66             if (@$element == 1) {
67             return $element;
68             } elsif (@$element == 2) {
69             if ( (reftype($element->[1]) // '') eq 'ARRAY') {
70             return [ $element->[0], {}, $element->[1] ];
71             } elsif (!$self->attributes and %{$element->[1]}) {
72             return [ $element->[0] ];
73             } else {
74             return $element;
75             }
76             } else {
77             if (!$self->attributes and %{$element->[1]}) {
78             return [ $element->[0], {}, $element->[2] ];
79             } else {
80             return $element;
81             }
82             }
83             # SimpleXML
84             } elsif ($type eq 'HASH') {
85             my $children = [
86             map {
87             my ($tag, $content) = ($_, $element->{$_});
88             # text
89             if (!ref $content) {
90             [ $tag, {}, [$content] ]
91             } elsif (reftype($content) eq 'ARRAY') {
92             @$content
93             ? map { [ $tag, {}, [$_] ] } @$content
94             : [ $tag ];
95             } elsif (reftype $content eq 'HASH' ) {
96             [ $tag, {}, [ $content ] ];
97             } else {
98             ();
99             }
100             }
101             grep { defined $element->{$_} }
102             sort keys %$element
103             ];
104             return $name ? [ $name, {}, $children ] : @$children;
105             }
106             }
107              
108             croak "expected XML as ARRAY or HASH reference";
109             }
110              
111             sub writeElement {
112             my $self = shift;
113            
114             foreach my $element (@_) {
115             $self->writeStartElement($element);
116             foreach my $child ( @{ $element->[2] // [] } ) {
117             if (ref $child) {
118             $self->writeElement( $self->microXML($child) );
119             } else {
120             $self->writeCharacters($child);
121             }
122             }
123              
124             $self->writeEndElement($element);
125             }
126             }
127              
128             sub writeStartElement {
129             my ($self, $element) = @_;
130              
131             my $args = { Name => $element->[0] };
132             $args->{Attributes} = $element->[1] if $element->[1];
133              
134             $self->handler->start_element($args);
135             }
136              
137             sub writeEndElement {
138             my ($self, $element) = @_;
139             $self->handler->end_element({ Name => $element->[0] });
140             }
141              
142             sub writeCharacters {
143             $_[0]->handler->characters({ Data => $_[1] });
144             }
145              
146             sub writeStart {
147             my $self = shift;
148             $self->handler->start_document;
149             if ($self->handler->can('xml_decl') && $self->xmldecl) {
150             $self->handler->xml_decl({
151             Version => $self->version,
152             Encoding => $self->encoding,
153             Standalone => $self->standalone,
154             });
155             }
156             $self->writeStartElement(@_) if @_;
157             }
158              
159             sub writeEnd {
160             my $self = shift;
161             $self->writeEndElement(@_) if @_;
162             $self->handler->end_document;
163             }
164              
165             1;
166             __END__