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