line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package XML::MyXML; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
169992
|
use 5.008001; |
|
2
|
|
|
|
|
17
|
|
4
|
2
|
|
|
2
|
|
13
|
use strict; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
41
|
|
5
|
2
|
|
|
2
|
|
12
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
59
|
|
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
877
|
use XML::MyXML::Object; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
72
|
|
8
|
|
|
|
|
|
|
|
9
|
2
|
|
|
2
|
|
12
|
use Encode; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
136
|
|
10
|
2
|
|
|
2
|
|
13
|
use Carp; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
99
|
|
11
|
2
|
|
|
2
|
|
11
|
use Scalar::Util qw/ weaken /; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
9427
|
|
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
require Exporter; |
14
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
15
|
|
|
|
|
|
|
our @EXPORT_OK = qw(tidy_xml object_to_xml xml_to_object simple_to_xml xml_to_simple check_xml xml_escape); |
16
|
|
|
|
|
|
|
our %EXPORT_TAGS = (all => [@EXPORT_OK]); |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $VERSION = "1.06"; |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my $DEFAULT_INDENTSTRING = ' ' x 4; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=encoding utf-8 |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
=head1 NAME |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
XML::MyXML - A simple-to-use XML module, for parsing and creating XML documents |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=head1 SYNOPSIS |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
use XML::MyXML qw(tidy_xml xml_to_object); |
31
|
|
|
|
|
|
|
use XML::MyXML qw(:all); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
my $xml = "- Table10.008.50
"; |
34
|
|
|
|
|
|
|
print tidy_xml($xml); |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
my $obj = xml_to_object($xml); |
37
|
|
|
|
|
|
|
print "Price in Euros = " . $obj->path('price/eur')->text; |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
$obj->simplify is hashref { item => { name => 'Table', price => { usd => '10.00', eur => '8.50' } } } |
40
|
|
|
|
|
|
|
$obj->simplify({ internal => 1 }) is hashref { name => 'Table', price => { usd => '10.00', eur => '8.50' } } |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 EXPORTABLE |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
xml_escape, tidy_xml, xml_to_object, object_to_xml, simple_to_xml, xml_to_simple, check_xml |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head1 FEATURES & LIMITATIONS |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
This module can parse XML comments, CDATA sections, XML entities (the standard five and numeric ones) and simple non-recursive C<< >>s |
49
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
It will ignore (won't parse) C<< >>, C<< ...?> >> and other C<< >> special markup |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
All strings (XML documents, attribute names, values, etc) produced by this module or passed as parameters to its functions, are strings that contain characters, rather than bytes/octets. Unless you use the C function flag (see below), in which case the XML documents (and just the XML documents) will be byte/octet strings. |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
XML documents to be parsed may not contain the C<< > >> character unencoded in attribute values |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=head1 OPTIONAL FUNCTION FLAGS |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
Some functions and methods in this module accept optional flags, listed under each function in the documentation. They are optional, default to zero unless stated otherwise, and can be used as follows: S 1, flag2 => 1 } ) >>>. This is what each flag does: |
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
C : the function will strip initial and ending whitespace from all text values returned |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
C : the function will expect the path to a file containing an XML document to parse, instead of an XML string |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
C : the function's XML output will include an XML declaration (C<< >>) in the beginning |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
C : the function will only return the contents of an element in a hashref instead of the element itself (see L for example) |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
C : the function will return tidy XML |
69
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
C : when producing tidy XML, this denotes the string with which child elements will be indented (Default is a string of 4 spaces) |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
C : the function (apart from doing what it's supposed to do) will also save its XML output in a file whose path is denoted by this flag |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
C : strip the namespaces (characters up to and including ':') from the tags |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
C : will add a link in the XML that's being output, of type 'text/xsl', pointing to the filename or URL denoted by this flag |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
C : the function will create a simple arrayref instead of a simple hashref (which will preserve order and elements with duplicate tags) |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
C : the XML document string which is parsed and/or produced by this function, should contain bytes/octets rather than characters |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
=head1 FUNCTIONS |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
=cut |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
sub _encode { |
87
|
91
|
|
|
91
|
|
139
|
my $string = shift; |
88
|
91
|
|
50
|
|
|
255
|
my $entities = shift || {}; |
89
|
91
|
50
|
|
|
|
168
|
defined $string or $string = ''; |
90
|
91
|
|
|
|
|
288
|
my %replace = ( |
91
|
|
|
|
|
|
|
'<' => '<', |
92
|
|
|
|
|
|
|
'>' => '>', |
93
|
|
|
|
|
|
|
'&' => '&', |
94
|
|
|
|
|
|
|
'\'' => ''', |
95
|
|
|
|
|
|
|
'"' => '"', |
96
|
|
|
|
|
|
|
); |
97
|
91
|
|
|
|
|
316
|
my $keys = "(".join("|", sort {length($b) <=> length($a)} keys %replace).")"; |
|
728
|
|
|
|
|
1063
|
|
98
|
91
|
|
|
|
|
2042
|
$string =~ s/$keys/$replace{$1}/g; |
99
|
91
|
|
|
|
|
553
|
return $string; |
100
|
|
|
|
|
|
|
} |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=head2 xml_escape($string) |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Returns the same string, but with the C<< < >>, C<< > >>, C<< & >>, C<< " >> and C<< ' >> characters replaced by their XML entities (e.g. C<< & >>). |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=cut |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
sub xml_escape { |
109
|
1
|
|
|
1
|
1
|
3
|
my ($string) = @_; |
110
|
|
|
|
|
|
|
|
111
|
1
|
|
|
|
|
4
|
return _encode($string); |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
sub _decode { |
115
|
130
|
|
|
130
|
|
206
|
my $string = shift; |
116
|
130
|
|
100
|
|
|
289
|
my $entities = shift || {}; |
117
|
130
|
|
50
|
|
|
369
|
my $flags = shift || {}; |
118
|
130
|
50
|
|
|
|
244
|
defined $string or $string = ''; |
119
|
130
|
|
|
|
|
567
|
my %replace = ( %$entities, reverse( |
120
|
|
|
|
|
|
|
'<' => '<', |
121
|
|
|
|
|
|
|
'>' => '>', |
122
|
|
|
|
|
|
|
'&' => '&', |
123
|
|
|
|
|
|
|
'\'' => ''', |
124
|
|
|
|
|
|
|
'"' => '"', |
125
|
|
|
|
|
|
|
)); |
126
|
130
|
|
|
|
|
648
|
my @capture = map "\Q$_\E", keys %replace; |
127
|
130
|
|
|
|
|
279
|
push @capture, '[0-9A-Fa-f]+;', '[0-9]+;'; |
128
|
130
|
|
|
|
|
380
|
my $capture = "(".join("|", @capture).")"; |
129
|
130
|
|
|
|
|
5265
|
my @captured = $string =~ /$capture/g; |
130
|
130
|
100
|
|
|
|
996
|
@captured or return $string; |
131
|
6
|
|
|
|
|
11
|
my %conv; |
132
|
6
|
|
|
|
|
13
|
foreach my $e (@captured) { |
133
|
6
|
50
|
|
|
|
18
|
if (exists $conv{$e}) { next; } |
|
0
|
|
|
|
|
0
|
|
134
|
6
|
100
|
|
|
|
19
|
if (exists $replace{$e}) { |
|
|
50
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
135
|
5
|
|
|
|
|
15
|
$conv{$e} = $replace{$e}; |
136
|
|
|
|
|
|
|
} elsif ($e =~ /\A([0-9a-fA-F]+);\z/) { |
137
|
1
|
|
|
|
|
8
|
$conv{$e} = chr(hex($1)); |
138
|
|
|
|
|
|
|
} elsif ($e =~ /\A([0-9]+);\z/) { |
139
|
0
|
|
|
|
|
0
|
$conv{$e} = chr($1); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
} |
142
|
6
|
|
|
|
|
34
|
my $keys = "(".join("|", map "\Q$_\E", keys %conv).")"; |
143
|
6
|
|
|
|
|
78
|
$string =~ s/$keys/$conv{$1}/g; |
144
|
6
|
|
|
|
|
65
|
return $string; |
145
|
|
|
|
|
|
|
} |
146
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
sub _strip { |
148
|
2
|
|
|
2
|
|
4
|
my $string = shift; |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
# NOTE: Replace this with the 'r' flag of the substitution operator |
151
|
2
|
50
|
|
|
|
16
|
return defined $string ? ($string =~ /\A\s*(.*?)\s*\z/s)[0] : $string; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
sub _strip_ns { |
155
|
1
|
|
|
1
|
|
2
|
my $string = shift; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# NOTE: Replace this with the 'r' flag of the substitution operator |
158
|
1
|
50
|
|
|
|
10
|
return defined $string ? ($string =~ /\A(?:.+\:)?(.*)\z/s)[0] : $string; |
159
|
|
|
|
|
|
|
} |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
=head2 tidy_xml($raw_xml) |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
Returns the XML string in a tidy format (with tabs & newlines) |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
Optional flags: C, C, C, C, C |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
=cut |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
sub tidy_xml { |
171
|
3
|
|
|
3
|
1
|
7
|
my $xml = shift; |
172
|
3
|
|
50
|
|
|
13
|
my $flags = shift || {}; |
173
|
|
|
|
|
|
|
|
174
|
3
|
|
|
|
|
8
|
my $object = xml_to_object($xml, $flags); |
175
|
3
|
50
|
|
|
|
10
|
defined $object or return $object; |
176
|
3
|
|
|
|
|
13
|
_tidy_object($object, undef, $flags); |
177
|
3
|
|
|
|
|
23
|
my $return = $object->to_xml({ %$flags, tidy => 0 }) . "\n"; |
178
|
3
|
|
|
|
|
26
|
return $return; |
179
|
|
|
|
|
|
|
} |
180
|
|
|
|
|
|
|
|
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
=head2 xml_to_object($raw_xml) |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
Creates an 'XML::MyXML::Object' object from the raw XML provided |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
Optional flags: C, C |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=cut |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
sub xml_to_object { |
191
|
40
|
|
|
40
|
1
|
3752
|
my $xml = shift; |
192
|
40
|
|
100
|
|
|
122
|
my $flags = shift || {}; |
193
|
|
|
|
|
|
|
|
194
|
40
|
100
|
|
|
|
103
|
if ($flags->{file}) { |
195
|
1
|
50
|
|
|
|
37
|
open my $fh, '<', $xml or croak "Error: The file '$xml' could not be opened for reading: $!"; |
196
|
1
|
|
|
|
|
20
|
$xml = join '', <$fh>; |
197
|
1
|
|
|
|
|
12
|
close $fh; |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
40
|
100
|
100
|
|
|
137
|
if ($flags->{bytes} or $flags->{file}) { |
201
|
5
|
|
|
|
|
24
|
my (undef, undef, $encoding) = $xml =~ /<\?xml(\s[^>]+)?\sencoding=(['"])(.*?)\2/g; |
202
|
5
|
50
|
|
|
|
15
|
$encoding = 'UTF-8' if ! defined $encoding; |
203
|
5
|
50
|
|
|
|
33
|
if ($encoding =~ /\Autf-?8\z/i) { $encoding = 'UTF-8'; } |
|
5
|
|
|
|
|
10
|
|
204
|
5
|
|
|
|
|
9
|
eval { |
205
|
5
|
|
|
|
|
20
|
$xml = decode($encoding, $xml, Encode::FB_CROAK); |
206
|
|
|
|
|
|
|
}; |
207
|
5
|
100
|
|
|
|
593
|
! $@ or croak 'Error: Input string is invalid UTF-8'; |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
39
|
|
|
|
|
64
|
my $entities = {}; |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# Parse CDATA sections |
213
|
39
|
|
|
|
|
108
|
$xml =~ s/<\!\[CDATA\[(.*?)\]\]>/_encode($1)/egs; |
|
0
|
|
|
|
|
0
|
|
214
|
39
|
|
|
|
|
481
|
my @els = $xml =~ /(|$)|<[^>]*?>|[^<>]+)/sg; |
215
|
|
|
|
|
|
|
# Remove comments, special markup and initial whitespace |
216
|
|
|
|
|
|
|
{ |
217
|
39
|
|
|
|
|
69
|
my $init_ws = 1; |
|
39
|
|
|
|
|
50
|
|
218
|
39
|
|
|
|
|
75
|
foreach my $el (@els) { |
219
|
304
|
50
|
|
|
|
1033
|
if ($el =~ /\A\z/) { croak encode_utf8("Error: unclosed XML comment block - '$el'"); } |
|
0
|
|
|
|
|
0
|
|
221
|
0
|
|
|
|
|
0
|
undef $el; |
222
|
|
|
|
|
|
|
} elsif ($el =~ /\A<\?/) { # like or |
223
|
0
|
0
|
|
|
|
0
|
if ($el !~ /\?>\z/) { croak encode_utf8("Error: Erroneous special markup - '$el'"); } |
|
0
|
|
|
|
|
0
|
|
224
|
0
|
|
|
|
|
0
|
undef $el; |
225
|
|
|
|
|
|
|
} elsif (my ($entname, undef, $entvalue) = $el =~ /\A\z/g) { |
226
|
2
|
|
|
|
|
45
|
$entities->{"&$entname;"} = _decode($entvalue); |
227
|
2
|
|
|
|
|
7
|
undef $el; |
228
|
|
|
|
|
|
|
} elsif ($el =~ / or or |
229
|
0
|
|
|
|
|
0
|
undef $el; |
230
|
|
|
|
|
|
|
} elsif ($init_ws) { |
231
|
42
|
100
|
|
|
|
131
|
if ($el =~ /\S/) { |
232
|
39
|
|
|
|
|
74
|
$init_ws = 0; |
233
|
|
|
|
|
|
|
} else { |
234
|
3
|
|
|
|
|
42
|
undef $el; |
235
|
|
|
|
|
|
|
} |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
} |
238
|
39
|
|
|
|
|
76
|
@els = grep { defined $_ } @els; |
|
304
|
|
|
|
|
551
|
|
239
|
39
|
50
|
|
|
|
98
|
if (! @els) { croak "Error: No elements in XML document"; } |
|
0
|
|
|
|
|
0
|
|
240
|
|
|
|
|
|
|
} |
241
|
39
|
|
|
|
|
56
|
my @stack; |
242
|
39
|
|
|
|
|
116
|
my $object = bless ({ content => [] }, 'XML::MyXML::Object'); |
243
|
39
|
|
|
|
|
57
|
my $pointer = $object; |
244
|
39
|
|
|
|
|
62
|
foreach my $el (@els) { |
245
|
299
|
50
|
|
|
|
1540
|
if ($el =~ /\A<\/?>\z/) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
246
|
0
|
|
|
|
|
0
|
croak encode_utf8("Error: Strange element: '$el'"); |
247
|
|
|
|
|
|
|
} elsif ($el =~ /\A<\/[^\s>]+>\z/) { |
248
|
97
|
|
|
|
|
325
|
my ($element) = $el =~ /\A<\/(\S+)>\z/g; |
249
|
97
|
50
|
|
|
|
237
|
if (! length($element)) { croak encode_utf8("Error: Strange element: '$el'"); } |
|
0
|
|
|
|
|
0
|
|
250
|
97
|
50
|
|
|
|
229
|
if ($stack[-1]{element} ne $element) { croak encode_utf8("Error: Incompatible stack element: stack='".$stack[-1]{element}."' element='$el'"); } |
|
0
|
|
|
|
|
0
|
|
251
|
97
|
|
|
|
|
148
|
my $stackentry = pop @stack; |
252
|
97
|
100
|
|
|
|
129
|
if ($#{$stackentry->{content}} == -1) { |
|
97
|
|
|
|
|
213
|
|
253
|
1
|
|
|
|
|
3
|
delete $stackentry->{content}; |
254
|
|
|
|
|
|
|
} |
255
|
97
|
|
|
|
|
219
|
$pointer = $stackentry->{parent}; |
256
|
|
|
|
|
|
|
} elsif ($el =~ /\A<[^>]+\/>\z/) { |
257
|
7
|
|
|
|
|
32
|
my ($element) = $el =~ /\A<([^\s>\/]+)/g; |
258
|
7
|
50
|
|
|
|
22
|
if (! length($element)) { croak encode_utf8("Error: Strange element: '$el'"); } |
|
0
|
|
|
|
|
0
|
|
259
|
7
|
|
|
|
|
74
|
$el =~ s/\A<\Q$element\E//; |
260
|
7
|
|
|
|
|
28
|
$el =~ s/\/>\z//; |
261
|
7
|
|
|
|
|
18
|
my @attrs = $el =~ /\s+(\S+=(['"]).*?\2)/g; |
262
|
7
|
|
|
|
|
11
|
my $i = 1; |
263
|
7
|
|
|
|
|
13
|
@attrs = grep {$i++ % 2} @attrs; |
|
0
|
|
|
|
|
0
|
|
264
|
7
|
|
|
|
|
9
|
my %attr; |
265
|
7
|
|
|
|
|
15
|
foreach my $attr (@attrs) { |
266
|
0
|
|
|
|
|
0
|
my ($name, undef, $value) = $attr =~ /\A(\S+?)=(['"])(.*?)\2\z/g; |
267
|
0
|
0
|
0
|
|
|
0
|
if (! length($name) or ! defined($value)) { croak encode_utf8("Error: Strange attribute: '$attr'"); } |
|
0
|
|
|
|
|
0
|
|
268
|
0
|
|
|
|
|
0
|
$attr{$name} = _decode($value, $entities); |
269
|
|
|
|
|
|
|
} |
270
|
7
|
|
|
|
|
21
|
my $entry = { element => $element, attrs => \%attr, parent => $pointer }; |
271
|
7
|
|
|
|
|
25
|
weaken( $entry->{parent} ); |
272
|
7
|
|
|
|
|
9
|
bless $entry, 'XML::MyXML::Object'; |
273
|
7
|
|
|
|
|
21
|
push @{$pointer->{content}}, $entry; |
|
7
|
|
|
|
|
24
|
|
274
|
|
|
|
|
|
|
} elsif ($el =~ /\A<[^\s>\/][^>]*>\z/) { |
275
|
98
|
|
|
|
|
338
|
my ($element) = $el =~ /\A<([^\s>]+)/g; |
276
|
98
|
50
|
|
|
|
256
|
if (! length($element)) { croak encode_utf8("Error: Strange element: '$el'"); } |
|
0
|
|
|
|
|
0
|
|
277
|
98
|
|
|
|
|
1198
|
$el =~ s/\A<\Q$element\E//; |
278
|
98
|
|
|
|
|
373
|
$el =~ s/>\z//; |
279
|
98
|
|
|
|
|
282
|
my @attrs = $el =~ /\s+(\S+=(['"]).*?\2)/g; |
280
|
98
|
|
|
|
|
153
|
my $i = 1; |
281
|
98
|
|
|
|
|
152
|
@attrs = grep {$i++ % 2} @attrs; |
|
60
|
|
|
|
|
112
|
|
282
|
98
|
|
|
|
|
130
|
my %attr; |
283
|
98
|
|
|
|
|
166
|
foreach my $attr (@attrs) { |
284
|
30
|
|
|
|
|
156
|
my ($name, undef, $value) = $attr =~ /\A(\S+?)=(['"])(.*?)\2\z/g; |
285
|
30
|
50
|
33
|
|
|
135
|
if (! length($name) or ! defined($value)) { croak encode_utf8("Error: Strange attribute: '$attr'"); } |
|
0
|
|
|
|
|
0
|
|
286
|
30
|
|
|
|
|
58
|
$attr{$name} = _decode($value, $entities); |
287
|
|
|
|
|
|
|
} |
288
|
98
|
|
|
|
|
358
|
my $entry = { element => $element, attrs => \%attr, content => [], parent => $pointer }; |
289
|
98
|
|
|
|
|
325
|
weaken( $entry->{parent} ); |
290
|
98
|
|
|
|
|
145
|
bless $entry, 'XML::MyXML::Object'; |
291
|
98
|
|
|
|
|
152
|
push @stack, $entry; |
292
|
98
|
|
|
|
|
122
|
push @{$pointer->{content}}, $entry; |
|
98
|
|
|
|
|
189
|
|
293
|
98
|
|
|
|
|
243
|
$pointer = $entry; |
294
|
|
|
|
|
|
|
} elsif ($el =~ /\A[^<>]*\z/) { |
295
|
97
|
|
|
|
|
222
|
my $entry = { value => _decode($el, $entities), parent => $pointer }; |
296
|
97
|
|
|
|
|
342
|
weaken( $entry->{parent} ); |
297
|
97
|
|
|
|
|
191
|
bless $entry, 'XML::MyXML::Object'; |
298
|
97
|
|
|
|
|
132
|
push @{$pointer->{content}}, $entry; |
|
97
|
|
|
|
|
269
|
|
299
|
|
|
|
|
|
|
} else { |
300
|
0
|
|
|
|
|
0
|
croak encode_utf8("Error: Strange element: '$el'"); |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
} |
303
|
39
|
100
|
|
|
|
83
|
if (@stack) { croak encode_utf8("Error: The <$stack[-1]{element}> element has not been closed in XML"); } |
|
1
|
|
|
|
|
7
|
|
304
|
38
|
|
|
|
|
66
|
$object = $object->{content}[0]; |
305
|
38
|
|
|
|
|
64
|
$object->{parent} = undef; |
306
|
38
|
|
|
|
|
236
|
return $object; |
307
|
|
|
|
|
|
|
} |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
sub _objectarray_to_xml { |
310
|
60
|
|
|
60
|
|
78
|
my $object = shift; |
311
|
|
|
|
|
|
|
|
312
|
60
|
|
|
|
|
112
|
my $xml = ''; |
313
|
60
|
|
|
|
|
84
|
foreach my $stuff (@$object) { |
314
|
78
|
100
|
66
|
|
|
216
|
if (! defined $stuff->{element} and defined $stuff->{value}) { |
315
|
32
|
|
|
|
|
58
|
$xml .= _encode($stuff->{value}); |
316
|
|
|
|
|
|
|
} else { |
317
|
46
|
|
|
|
|
106
|
$xml .= "<".$stuff->{element}; |
318
|
46
|
|
|
|
|
54
|
foreach my $attrname (keys %{$stuff->{attrs}}) { |
|
46
|
|
|
|
|
111
|
|
319
|
1
|
|
|
|
|
5
|
$xml .= " ".$attrname.'="'._encode($stuff->{attrs}{$attrname}).'"'; |
320
|
|
|
|
|
|
|
} |
321
|
46
|
100
|
100
|
|
|
108
|
if (! defined $stuff->{content} or ! @{ $stuff->{content} }) { |
322
|
7
|
|
|
|
|
14
|
$xml .= "/>" |
323
|
|
|
|
|
|
|
} else { |
324
|
39
|
|
|
|
|
70
|
$xml .= ">"; |
325
|
39
|
|
|
|
|
82
|
$xml .= _objectarray_to_xml($stuff->{content}); |
326
|
39
|
|
|
|
|
107
|
$xml .= "".$stuff->{element}.">"; |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
} |
329
|
|
|
|
|
|
|
} |
330
|
60
|
|
|
|
|
133
|
return $xml; |
331
|
|
|
|
|
|
|
} |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
=head2 object_to_xml($object) |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Creates an XML string from the 'XML::MyXML::Object' object provided |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
Optional flags: C, C, C, C, C |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
=cut |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
sub object_to_xml { |
342
|
0
|
|
|
0
|
1
|
0
|
my $object = shift; |
343
|
0
|
|
0
|
|
|
0
|
my $flags = shift || {}; |
344
|
|
|
|
|
|
|
|
345
|
0
|
|
|
|
|
0
|
return $object->to_xml( $flags ); |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
sub _tidy_object { |
349
|
19
|
|
|
19
|
|
30
|
my $object = shift; |
350
|
19
|
|
100
|
|
|
39
|
my $tabs = shift || 0; |
351
|
19
|
|
50
|
|
|
34
|
my $flags = shift || {}; |
352
|
|
|
|
|
|
|
|
353
|
19
|
100
|
|
|
|
39
|
my $indentstring = exists $flags->{indentstring} ? $flags->{indentstring} : $DEFAULT_INDENTSTRING; |
354
|
|
|
|
|
|
|
|
355
|
19
|
100
|
66
|
|
|
40
|
if (! defined $object->{content} or ! @{$object->{content}}) { return; } |
|
9
|
|
|
|
|
26
|
|
|
10
|
|
|
|
|
23
|
|
356
|
9
|
|
|
|
|
14
|
my $hastext; |
357
|
9
|
|
|
|
|
10
|
my @children = @{$object->{content}}; |
|
9
|
|
|
|
|
21
|
|
358
|
9
|
|
|
|
|
23
|
foreach my $i (0..$#children) { |
359
|
11
|
|
|
|
|
50
|
my $child = $children[$i]; |
360
|
11
|
100
|
|
|
|
29
|
if (defined $child->{value}) { |
361
|
5
|
50
|
|
|
|
21
|
if ($child->{value} =~ /\S/) { |
362
|
5
|
|
|
|
|
8
|
$hastext = 1; |
363
|
5
|
|
|
|
|
9
|
last; |
364
|
|
|
|
|
|
|
} |
365
|
|
|
|
|
|
|
} |
366
|
|
|
|
|
|
|
} |
367
|
9
|
100
|
|
|
|
16
|
if ($hastext) { return; } |
|
5
|
|
|
|
|
11
|
|
368
|
|
|
|
|
|
|
|
369
|
4
|
|
33
|
|
|
7
|
@{$object->{content}} = grep { ! defined $_->{value} or $_->{value} !~ /\A\s*\z/ } @{$object->{content}}; |
|
4
|
|
|
|
|
9
|
|
|
6
|
|
|
|
|
20
|
|
|
4
|
|
|
|
|
9
|
|
370
|
|
|
|
|
|
|
|
371
|
4
|
|
|
|
|
7
|
@children = @{$object->{content}}; |
|
4
|
|
|
|
|
7
|
|
372
|
4
|
|
|
|
|
9
|
$object->{content} = []; |
373
|
4
|
|
|
|
|
10
|
for my $i (0..$#children) { |
374
|
6
|
|
|
|
|
58
|
my $whitespace = bless ({ value => "\n".($indentstring x ($tabs+1)), parent => $object }, 'XML::MyXML::Object'); |
375
|
6
|
|
|
|
|
21
|
weaken( $whitespace->{parent} ); |
376
|
6
|
|
|
|
|
7
|
push @{$object->{content}}, $whitespace; |
|
6
|
|
|
|
|
13
|
|
377
|
6
|
|
|
|
|
8
|
push @{$object->{content}}, $children[$i]; |
|
6
|
|
|
|
|
14
|
|
378
|
|
|
|
|
|
|
} |
379
|
4
|
|
|
|
|
24
|
my $whitespace = bless ({ value => "\n".($indentstring x ($tabs)), parent => $object }, 'XML::MyXML::Object'); |
380
|
4
|
|
|
|
|
13
|
weaken( $whitespace->{parent} ); |
381
|
4
|
|
|
|
|
6
|
push @{$object->{content}}, $whitespace; |
|
4
|
|
|
|
|
7
|
|
382
|
|
|
|
|
|
|
|
383
|
4
|
|
|
|
|
6
|
for my $i (0..$#{$object->{content}}) { |
|
4
|
|
|
|
|
13
|
|
384
|
16
|
|
|
|
|
43
|
_tidy_object($object->{content}[$i], $tabs+1, $flags); |
385
|
|
|
|
|
|
|
} |
386
|
|
|
|
|
|
|
} |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
=head2 simple_to_xml($simple_array_ref) |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
Produces a raw XML string from either an array reference, a hash reference or a mixed structure such as these examples: |
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
{ thing => { name => 'John', location => { city => 'New York', country => 'U.S.A.' } } } |
394
|
|
|
|
|
|
|
# JohnU.S.A.New York |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
[ thing => [ name => 'John', location => [ city => 'New York', country => 'U.S.A.' ] ] ] |
397
|
|
|
|
|
|
|
# JohnU.S.A.New York |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
{ thing => { name => 'John', location => [ city => 'New York', city => 'Boston', country => 'U.S.A.' ] } } |
400
|
|
|
|
|
|
|
# JohnNew YorkBostonU.S.A. |
401
|
|
|
|
|
|
|
|
402
|
|
|
|
|
|
|
Here's a mini-tutorial on how to use this function, in which you'll also see how to set attributes. |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
The simplest invocations are these: |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
simple_to_xml({target => undef}) |
407
|
|
|
|
|
|
|
# |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
simple_to_xml({target => 123}) |
410
|
|
|
|
|
|
|
# 123 |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
Every set of sibling elements (such as the document itself, which is a single top-level element, or a pack of |
413
|
|
|
|
|
|
|
5 elements all children to the same parent element) is represented in the $simple_array_ref parameter as |
414
|
|
|
|
|
|
|
key-value pairs inside either a hashref or an arrayref (you can choose which). |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
Keys represent tags+attributes of the sibling elements, whereas values represent the contents of those elements. |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
Eg: |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
[ |
421
|
|
|
|
|
|
|
first => 'John', |
422
|
|
|
|
|
|
|
last => 'Doe,' |
423
|
|
|
|
|
|
|
] |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
...and... |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
{ |
428
|
|
|
|
|
|
|
first => 'John', |
429
|
|
|
|
|
|
|
last => 'Doe', |
430
|
|
|
|
|
|
|
} |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
both translate to: |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
JohnDoe |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
A value can either be undef (to denote an empty element), or a string (to denote a string), or another |
437
|
|
|
|
|
|
|
hashref/arrayref to denote a set of children elements, like this: |
438
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
{ |
440
|
|
|
|
|
|
|
person => { |
441
|
|
|
|
|
|
|
name => { |
442
|
|
|
|
|
|
|
first => 'John', |
443
|
|
|
|
|
|
|
last => 'Doe' |
444
|
|
|
|
|
|
|
} |
445
|
|
|
|
|
|
|
} |
446
|
|
|
|
|
|
|
} |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
...becomes: |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
John |
453
|
|
|
|
|
|
|
Doe |
454
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
|
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
The only difference between using an arrayref or using a hashref, is that arrayrefs preserve the |
459
|
|
|
|
|
|
|
order of the elements, and allow repetition of identical tags. So a person with many addresses, should choose to |
460
|
|
|
|
|
|
|
represent its list of addresses under an arrayref, like this: |
461
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
{ |
463
|
|
|
|
|
|
|
person => [ |
464
|
|
|
|
|
|
|
name => { |
465
|
|
|
|
|
|
|
first => 'John', |
466
|
|
|
|
|
|
|
last => 'Doe', |
467
|
|
|
|
|
|
|
}, |
468
|
|
|
|
|
|
|
address => { |
469
|
|
|
|
|
|
|
country => 'Malta', |
470
|
|
|
|
|
|
|
}, |
471
|
|
|
|
|
|
|
address => { |
472
|
|
|
|
|
|
|
country => 'Indonesia', |
473
|
|
|
|
|
|
|
}, |
474
|
|
|
|
|
|
|
address => { |
475
|
|
|
|
|
|
|
country => 'China', |
476
|
|
|
|
|
|
|
} |
477
|
|
|
|
|
|
|
] |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
...which becomes: |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
Doe |
485
|
|
|
|
|
|
|
John |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
Malta |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
Indonesia |
492
|
|
|
|
|
|
|
|
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
China |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
Finally, to set attributes to your elements (eg id="12") you need to replace the key with either |
499
|
|
|
|
|
|
|
a string containing attributes as well (eg: C<'address id="12"'>), or replace it with a reference, as the many |
500
|
|
|
|
|
|
|
items in the examples below: |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
{thing => [ |
503
|
|
|
|
|
|
|
'item id="1"' => 'chair', |
504
|
|
|
|
|
|
|
[item => {id => 2}] => 'table', |
505
|
|
|
|
|
|
|
[item => [id => 3]] => 'door', |
506
|
|
|
|
|
|
|
[item => id => 4] => 'sofa', |
507
|
|
|
|
|
|
|
{item => {id => 5}} => 'bed', |
508
|
|
|
|
|
|
|
{item => [id => 6]} => 'shirt', |
509
|
|
|
|
|
|
|
[item => {id => 7, other => 8}, [more => 9, also => 10, but_not => undef]] => 'towel' |
510
|
|
|
|
|
|
|
]} |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
...which becomes: |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
- chair
|
516
|
|
|
|
|
|
|
- table
|
517
|
|
|
|
|
|
|
- door
|
518
|
|
|
|
|
|
|
- sofa
|
519
|
|
|
|
|
|
|
- bed
|
520
|
|
|
|
|
|
|
- shirt
|
521
|
|
|
|
|
|
|
- towel
|
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
As you see, attributes may be represented in a great variety of ways, so you don't need to remember |
525
|
|
|
|
|
|
|
the "correct" one. |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
Of course if the "simple structure" is a hashref, the key cannot be a reference (because hash keys are always |
528
|
|
|
|
|
|
|
strings), so if you want attributes on your elements, you either need the enclosing structure to be an |
529
|
|
|
|
|
|
|
arrayref as in the example above, to allow keys to be refs which contain the attributes, or you need to |
530
|
|
|
|
|
|
|
represent the key (=tag+attrs) as a string, like this (also in the previous example): C<'item id="1"'> |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
This concludes the mini-tutorial of the simple_to_xml function. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
All the strings in C<$simple_array_ref> need to contain characters, rather than bytes/octets. The C optional flag only affects the produced XML string. |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
Optional flags: C, C, C, C, C, C |
537
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
=cut |
539
|
|
|
|
|
|
|
|
540
|
|
|
|
|
|
|
sub simple_to_xml { |
541
|
21
|
|
|
21
|
1
|
6402
|
my $arref = shift; |
542
|
21
|
|
100
|
|
|
89
|
my $flags = shift || {}; |
543
|
|
|
|
|
|
|
|
544
|
21
|
|
|
|
|
37
|
my $xml = ''; |
545
|
21
|
100
|
|
|
|
87
|
my ($key, $value, @residue) = (ref $arref eq 'HASH') ? %$arref : @$arref; |
546
|
21
|
|
|
|
|
51
|
$key = _key_to_string($key); |
547
|
21
|
50
|
|
|
|
59
|
if (@residue) { croak "Error: the provided simple ref contains more than 1 top element"; } |
|
0
|
|
|
|
|
0
|
|
548
|
21
|
|
|
|
|
112
|
my ($tag) = $key =~ /\A(\S+)/g; |
549
|
21
|
50
|
|
|
|
53
|
croak encode_utf8("Error: Strange key: $key") if ! defined $tag; |
550
|
|
|
|
|
|
|
|
551
|
21
|
100
|
|
|
|
46
|
if (! ref $value) { |
552
|
12
|
50
|
33
|
|
|
45
|
if (defined $value and length $value) { |
553
|
12
|
|
|
|
|
30
|
$xml .= "<$key>"._encode($value)."$tag>"; |
554
|
|
|
|
|
|
|
} else { |
555
|
0
|
|
|
|
|
0
|
$xml .= "<$key/>"; |
556
|
|
|
|
|
|
|
} |
557
|
|
|
|
|
|
|
} else { |
558
|
9
|
|
|
|
|
28
|
$xml .= "<$key>"._arrayref_to_xml($value, $flags)."$tag>"; |
559
|
|
|
|
|
|
|
} |
560
|
21
|
0
|
|
|
|
56
|
if ($flags->{tidy}) { $xml = tidy_xml($xml, { exists $flags->{indentstring} ? (indentstring => $flags->{indentstring}) : () }); } |
|
0
|
50
|
|
|
|
0
|
|
561
|
21
|
50
|
|
|
|
50
|
my $decl = $flags->{complete} ? ''."\n" : ''; |
562
|
21
|
50
|
|
|
|
48
|
$decl .= "{xslt}\"?>\n" if $flags->{xslt}; |
563
|
21
|
|
|
|
|
40
|
$xml = $decl . $xml; |
564
|
|
|
|
|
|
|
|
565
|
21
|
100
|
|
|
|
50
|
if (defined $flags->{save}) { |
566
|
1
|
50
|
|
|
|
62
|
open my $fh, '>', $flags->{save} or croak "Error: Couldn't open file '$flags->{save}' for writing: $!"; |
567
|
1
|
|
|
1
|
|
10
|
binmode $fh, ':encoding(UTF-8)'; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
8
|
|
|
1
|
|
|
|
|
38
|
|
568
|
1
|
|
|
|
|
1301
|
print $fh $xml; |
569
|
1
|
|
|
|
|
152
|
close $fh; |
570
|
|
|
|
|
|
|
} |
571
|
|
|
|
|
|
|
|
572
|
21
|
100
|
|
|
|
47
|
$xml = encode_utf8($xml) if $flags->{bytes}; |
573
|
21
|
|
|
|
|
74
|
return $xml; |
574
|
|
|
|
|
|
|
} |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
sub _flatten { |
577
|
127
|
|
|
127
|
|
177
|
my ($thing) = @_; |
578
|
|
|
|
|
|
|
|
579
|
127
|
100
|
|
|
|
210
|
if (!ref $thing) { return $thing; } |
|
90
|
100
|
|
|
|
228
|
|
|
|
50
|
|
|
|
|
|
580
|
13
|
|
|
|
|
31
|
elsif (ref $thing eq 'HASH') { return map _flatten($_), %$thing; } |
581
|
24
|
|
|
|
|
56
|
elsif (ref $thing eq 'ARRAY') { return map _flatten($_), @$thing; } |
582
|
0
|
|
|
|
|
0
|
else { croak 'Error: reference of invalid type in simple_to_xml: '.(ref $thing); } |
583
|
|
|
|
|
|
|
} |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
sub _key_to_string { |
586
|
37
|
|
|
37
|
|
68
|
my ($key) = @_; |
587
|
|
|
|
|
|
|
|
588
|
37
|
100
|
|
|
|
70
|
if (! ref $key) { |
589
|
19
|
|
|
|
|
41
|
return $key; |
590
|
|
|
|
|
|
|
} else { |
591
|
18
|
|
|
|
|
32
|
my ($tag, %attrs) = _flatten($key); |
592
|
18
|
|
|
|
|
50
|
return $tag . join('', map ' '.$_.'="'._encode($attrs{$_}).'"', grep {defined $attrs{$_}} keys %attrs); |
|
36
|
|
|
|
|
94
|
|
593
|
|
|
|
|
|
|
} |
594
|
|
|
|
|
|
|
} |
595
|
|
|
|
|
|
|
|
596
|
|
|
|
|
|
|
sub _arrayref_to_xml { |
597
|
11
|
|
|
11
|
|
20
|
my $arref = shift; |
598
|
11
|
|
50
|
|
|
25
|
my $flags = shift || {}; |
599
|
|
|
|
|
|
|
|
600
|
11
|
|
|
|
|
21
|
my $xml = ''; |
601
|
|
|
|
|
|
|
|
602
|
11
|
100
|
|
|
|
29
|
if (ref $arref eq 'HASH') { return _hashref_to_xml($arref, $flags); } |
|
4
|
|
|
|
|
13
|
|
603
|
|
|
|
|
|
|
|
604
|
7
|
|
|
|
|
18
|
foreach (my $i = 0; $i <= $#$arref; ) { |
605
|
16
|
|
|
|
|
32
|
my $key = $arref->[$i++]; |
606
|
16
|
|
|
|
|
28
|
$key = _key_to_string($key); |
607
|
16
|
|
|
|
|
63
|
my ($tag) = $key =~ /\A(\S+)/g; |
608
|
16
|
50
|
|
|
|
43
|
croak encode_utf8("Error: Strange key: $key") if ! defined $tag; |
609
|
16
|
|
|
|
|
27
|
my $value = $arref->[$i++]; |
610
|
|
|
|
|
|
|
|
611
|
16
|
50
|
|
|
|
43
|
if ($key eq '!as_is') { |
|
|
100
|
|
|
|
|
|
612
|
0
|
0
|
|
|
|
0
|
$xml .= $value if check_xml($value); |
613
|
|
|
|
|
|
|
} elsif (! ref $value) { |
614
|
14
|
50
|
33
|
|
|
55
|
if (defined $value and length $value) { |
615
|
14
|
|
|
|
|
31
|
$xml .= "<$key>"._encode($value)."$tag>"; |
616
|
|
|
|
|
|
|
} else { |
617
|
0
|
|
|
|
|
0
|
$xml .= "<$key/>"; |
618
|
|
|
|
|
|
|
} |
619
|
|
|
|
|
|
|
} else { |
620
|
2
|
|
|
|
|
9
|
$xml .= "<$key>"._arrayref_to_xml($value, $flags)."$tag>"; |
621
|
|
|
|
|
|
|
} |
622
|
|
|
|
|
|
|
} |
623
|
7
|
|
|
|
|
32
|
return $xml; |
624
|
|
|
|
|
|
|
} |
625
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
|
627
|
|
|
|
|
|
|
sub _hashref_to_xml { |
628
|
4
|
|
|
4
|
|
7
|
my $hashref = shift; |
629
|
4
|
|
50
|
|
|
10
|
my $flags = shift || {}; |
630
|
|
|
|
|
|
|
|
631
|
4
|
|
|
|
|
7
|
my $xml = ''; |
632
|
|
|
|
|
|
|
|
633
|
4
|
|
|
|
|
19
|
while (my ($key, $value) = each %$hashref) { |
634
|
4
|
|
|
|
|
13
|
my ($tag) = $key =~ /\A(\S+)/g; |
635
|
4
|
50
|
|
|
|
11
|
croak encode_utf8("Error: Strange key: $key") if ! defined $tag; |
636
|
|
|
|
|
|
|
|
637
|
4
|
50
|
|
|
|
12
|
if ($key eq '!as_is') { |
|
|
50
|
|
|
|
|
|
638
|
0
|
0
|
|
|
|
0
|
$xml .= $value if check_xml($value); |
639
|
|
|
|
|
|
|
} elsif (! ref $value) { |
640
|
4
|
100
|
100
|
|
|
19
|
if (defined $value and length $value) { |
641
|
2
|
|
|
|
|
10
|
$xml .= "<$key>"._encode($value)."$tag>"; |
642
|
|
|
|
|
|
|
} else { |
643
|
2
|
|
|
|
|
10
|
$xml .= "<$key/>"; |
644
|
|
|
|
|
|
|
} |
645
|
|
|
|
|
|
|
} else { |
646
|
0
|
|
|
|
|
0
|
$xml .= "<$key>"._arrayref_to_xml($value, $flags)."$tag>"; |
647
|
|
|
|
|
|
|
} |
648
|
|
|
|
|
|
|
} |
649
|
4
|
|
|
|
|
16
|
return $xml; |
650
|
|
|
|
|
|
|
} |
651
|
|
|
|
|
|
|
|
652
|
|
|
|
|
|
|
=head2 xml_to_simple($raw_xml) |
653
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
Produces a very simple hash object from the raw XML string provided. An example hash object created thusly is this: |
655
|
|
|
|
|
|
|
S { name => 'John', location => { city => 'New York', country => 'U.S.A.' } } } >>> |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
B This function only works on very simple XML strings, i.e. children of an element may not consist of both |
658
|
|
|
|
|
|
|
text and elements (child elements will be discarded in that case). Also attributes in tags are ignored. |
659
|
|
|
|
|
|
|
|
660
|
|
|
|
|
|
|
Since the object created is a hashref (unless used with the C optional flag), duplicate keys will be |
661
|
|
|
|
|
|
|
discarded. |
662
|
|
|
|
|
|
|
|
663
|
|
|
|
|
|
|
All strings contained in the output simple structure will always contain characters rather than octets/bytes, |
664
|
|
|
|
|
|
|
regardless of the C optional flag. |
665
|
|
|
|
|
|
|
|
666
|
|
|
|
|
|
|
Optional flags: C, C, C, C, C, C |
667
|
|
|
|
|
|
|
|
668
|
|
|
|
|
|
|
=cut |
669
|
|
|
|
|
|
|
|
670
|
|
|
|
|
|
|
sub xml_to_simple { |
671
|
7
|
|
|
7
|
1
|
2586
|
my $xml = shift; |
672
|
7
|
|
100
|
|
|
34
|
my $flags = shift || {}; |
673
|
|
|
|
|
|
|
|
674
|
7
|
|
|
|
|
19
|
my $object = xml_to_object($xml, $flags); |
675
|
|
|
|
|
|
|
|
676
|
7
|
50
|
|
|
|
36
|
my $return = defined $object ? $object->simplify($flags) : $object; |
677
|
|
|
|
|
|
|
|
678
|
7
|
|
|
|
|
80
|
return $return; |
679
|
|
|
|
|
|
|
} |
680
|
|
|
|
|
|
|
|
681
|
|
|
|
|
|
|
sub _objectarray_to_simple { |
682
|
62
|
|
|
62
|
|
78
|
my $object = shift; |
683
|
62
|
|
50
|
|
|
104
|
my $flags = shift || {}; |
684
|
|
|
|
|
|
|
|
685
|
62
|
50
|
|
|
|
97
|
if (! defined $object) { return undef; } |
|
0
|
|
|
|
|
0
|
|
686
|
|
|
|
|
|
|
|
687
|
62
|
50
|
|
|
|
100
|
if ($flags->{arrayref}) { |
688
|
0
|
|
|
|
|
0
|
return _objectarray_to_simple_arrayref($object, $flags); |
689
|
|
|
|
|
|
|
} else { |
690
|
62
|
|
|
|
|
104
|
return _objectarray_to_simple_hashref($object, $flags); |
691
|
|
|
|
|
|
|
} |
692
|
|
|
|
|
|
|
} |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
sub _objectarray_to_simple_hashref { |
695
|
62
|
|
|
62
|
|
70
|
my $object = shift; |
696
|
62
|
|
50
|
|
|
97
|
my $flags = shift || {}; |
697
|
|
|
|
|
|
|
|
698
|
62
|
50
|
|
|
|
93
|
if (! defined $object) { return undef; } |
|
0
|
|
|
|
|
0
|
|
699
|
|
|
|
|
|
|
|
700
|
62
|
|
|
|
|
84
|
my $hashref = {}; |
701
|
|
|
|
|
|
|
|
702
|
62
|
|
|
|
|
90
|
foreach my $stuff (@$object) { |
703
|
113
|
100
|
|
|
|
235
|
if (defined $stuff->{element}) { |
|
|
50
|
|
|
|
|
|
704
|
48
|
|
|
|
|
66
|
my $key = $stuff->{element}; |
705
|
48
|
100
|
|
|
|
84
|
if ($flags->{strip_ns}) { $key = _strip_ns($key); } |
|
1
|
|
|
|
|
4
|
|
706
|
48
|
|
|
|
|
75
|
$hashref->{ $key } = _objectarray_to_simple($stuff->{content}, $flags); |
707
|
|
|
|
|
|
|
} elsif (defined $stuff->{value}) { |
708
|
65
|
|
|
|
|
92
|
my $value = $stuff->{value}; |
709
|
65
|
50
|
|
|
|
116
|
if ($flags->{strip}) { $value = _strip($value); } |
|
0
|
|
|
|
|
0
|
|
710
|
65
|
100
|
|
|
|
235
|
return $value if $value =~ /\S/; |
711
|
|
|
|
|
|
|
} |
712
|
|
|
|
|
|
|
} |
713
|
|
|
|
|
|
|
|
714
|
35
|
50
|
|
|
|
83
|
if (keys %$hashref) { |
715
|
35
|
|
|
|
|
90
|
return $hashref; |
716
|
|
|
|
|
|
|
} else { |
717
|
0
|
|
|
|
|
0
|
return undef; |
718
|
|
|
|
|
|
|
} |
719
|
|
|
|
|
|
|
} |
720
|
|
|
|
|
|
|
|
721
|
|
|
|
|
|
|
sub _objectarray_to_simple_arrayref { |
722
|
0
|
|
|
0
|
|
0
|
my $object = shift; |
723
|
0
|
|
0
|
|
|
0
|
my $flags = shift || {}; |
724
|
|
|
|
|
|
|
|
725
|
0
|
0
|
|
|
|
0
|
if (! defined $object) { return undef; } |
|
0
|
|
|
|
|
0
|
|
726
|
|
|
|
|
|
|
|
727
|
0
|
|
|
|
|
0
|
my $arrayref = []; |
728
|
|
|
|
|
|
|
|
729
|
0
|
|
|
|
|
0
|
foreach my $stuff (@$object) { |
730
|
0
|
0
|
|
|
|
0
|
if (defined $stuff->{element}) { |
|
|
0
|
|
|
|
|
|
731
|
0
|
|
|
|
|
0
|
my $key = $stuff->{element}; |
732
|
0
|
0
|
|
|
|
0
|
if ($flags->{strip_ns}) { $key = _strip_ns($key); } |
|
0
|
|
|
|
|
0
|
|
733
|
0
|
|
|
|
|
0
|
push @$arrayref, ( $key, _objectarray_to_simple($stuff->{content}, $flags) ); |
734
|
|
|
|
|
|
|
} elsif (defined $stuff->{value}) { |
735
|
0
|
|
|
|
|
0
|
my $value = $stuff->{value}; |
736
|
0
|
0
|
|
|
|
0
|
if ($flags->{strip}) { $value = _strip($value); } |
|
0
|
|
|
|
|
0
|
|
737
|
0
|
0
|
|
|
|
0
|
return $value if $value =~ /\S/; |
738
|
|
|
|
|
|
|
} |
739
|
|
|
|
|
|
|
} |
740
|
|
|
|
|
|
|
|
741
|
0
|
0
|
|
|
|
0
|
if (@$arrayref) { |
742
|
0
|
|
|
|
|
0
|
return $arrayref; |
743
|
|
|
|
|
|
|
} else { |
744
|
0
|
|
|
|
|
0
|
return undef; |
745
|
|
|
|
|
|
|
} |
746
|
|
|
|
|
|
|
} |
747
|
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
|
749
|
|
|
|
|
|
|
=head2 check_xml($raw_xml) |
750
|
|
|
|
|
|
|
|
751
|
|
|
|
|
|
|
Returns true if the $raw_xml string is valid XML (valid enough to be used by this module), and false otherwise. |
752
|
|
|
|
|
|
|
|
753
|
|
|
|
|
|
|
Optional flags: C, C |
754
|
|
|
|
|
|
|
|
755
|
|
|
|
|
|
|
=cut |
756
|
|
|
|
|
|
|
|
757
|
|
|
|
|
|
|
sub check_xml { |
758
|
2
|
|
|
2
|
1
|
338
|
my $xml = shift; |
759
|
2
|
|
50
|
|
|
12
|
my $flags = shift || {}; |
760
|
|
|
|
|
|
|
|
761
|
2
|
|
|
|
|
4
|
my $obj = eval { xml_to_object($xml, $flags) }; |
|
2
|
|
|
|
|
5
|
|
762
|
2
|
|
|
|
|
132
|
return ! $@; |
763
|
|
|
|
|
|
|
} |
764
|
|
|
|
|
|
|
|
765
|
|
|
|
|
|
|
1; # End of XML::MyXML |
766
|
|
|
|
|
|
|
|
767
|
|
|
|
|
|
|
__END__ |