line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package XML::FromArrayref; |
2
|
|
|
|
|
|
|
|
3
|
4
|
|
|
4
|
|
97283
|
use 5.006; |
|
4
|
|
|
|
|
15
|
|
|
4
|
|
|
|
|
145
|
|
4
|
4
|
|
|
4
|
|
23
|
use strict; |
|
4
|
|
|
|
|
7
|
|
|
4
|
|
|
|
|
136
|
|
5
|
4
|
|
|
4
|
|
20
|
use warnings; |
|
4
|
|
|
|
|
47
|
|
|
4
|
|
|
|
|
139
|
|
6
|
|
|
|
|
|
|
|
7
|
4
|
|
|
4
|
|
22
|
use base qw( Exporter ); |
|
4
|
|
|
|
|
6
|
|
|
4
|
|
|
|
|
648
|
|
8
|
|
|
|
|
|
|
our @EXPORT = qw( XML ); |
9
|
|
|
|
|
|
|
our @EXPORT_OK = qw( start_tag end_tag XMLdecl doctype ); |
10
|
|
|
|
|
|
|
our %EXPORT_TAGS = ( |
11
|
|
|
|
|
|
|
TAGS => [qw( start_tag end_tag )], |
12
|
|
|
|
|
|
|
PROLOG => [qw( XMLdecl doctype )] |
13
|
|
|
|
|
|
|
); |
14
|
|
|
|
|
|
|
|
15
|
4
|
|
|
4
|
|
3670
|
use HTML::Entities; |
|
4
|
|
|
|
|
25171
|
|
|
4
|
|
|
|
|
391
|
|
16
|
4
|
|
|
4
|
|
3207
|
use URI::Escape; |
|
4
|
|
|
|
|
5330
|
|
|
4
|
|
|
|
|
2733
|
|
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
XML::FromArrayref - Output XML described by a Perl data structure |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 VERSION |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
Version 1.02 |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=cut |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
our $VERSION = '1.02'; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 SYNOPSIS |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
use XML::FromArrayref; |
33
|
|
|
|
|
|
|
print XML [ html => [ head => [ title => 'My Web page' ] ], [ body => 'Hello' ] ]; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
=head1 EXPORT |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
This module exports an XML() function that lets you easily print valid XML without embedding it in your Perl code. |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head1 SUBROUTINES/METHODS |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=head2 XML(@) |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
Takes a list of strings and arrayrefs describing XML content and returns the XML string. The strings are encoded; each arrayref represents an XML element, as follows: |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
[ $tag_name, $attributes, @content ] |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head3 $tag_name |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
evaluates to an XML tag name. If $tag_name is false then the whole element is replaced by its content. |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
If an arrayref's first element is another arrayref instead of an tag name, then the value of the first item of that array will be included in the XML string but will not be encoded. This lets you include text in the XML that has already been entity-encoded. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=head3 $attributes |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
is an optional hashref defining the element's attributes. If an attribute's value is undefined then the attribute will not appear in the generated XML string. Attribute values will be encoded. If there isn't a hashref in the second spot in the element-definition list then the element won't have any attributes in the generated XML. |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head3 @content |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
is another list of strings and arrayrefs, which will be used to generate the content of the element. If the content list is empty, then the element has no content and will be represented in the generated XML string by a single empty-element tag. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=cut |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
sub XML (@) { |
64
|
19
|
100
|
|
|
|
181
|
join '', grep defined $_, map { |
65
|
15
|
|
|
15
|
1
|
2165
|
ref $_ eq 'ARRAY' ? element( @$_ ) : encode_entities( $_, '&<' ) |
66
|
|
|
|
|
|
|
} @_; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
=head2 element() |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
Recursively renders XML elements from arrayrefs. |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
=cut |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
sub element { |
76
|
11
|
|
|
11
|
1
|
20
|
my ( $tag_name, $attributes, @content ) = @_; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# If an element's name is an array ref then it's |
79
|
|
|
|
|
|
|
# really text to print without encoding |
80
|
11
|
100
|
|
|
|
31
|
return $tag_name->[0] if ref $tag_name eq 'ARRAY'; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# If the second item in the list is not a hashref, |
83
|
|
|
|
|
|
|
# then the element has no attributes |
84
|
10
|
100
|
100
|
|
|
42
|
if ( defined $attributes and ref $attributes ne 'HASH' ) { |
85
|
6
|
|
|
|
|
9
|
unshift @content, $attributes; |
86
|
6
|
|
|
|
|
7
|
undef $attributes; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
# If the first expression in the list is false, then skip |
90
|
|
|
|
|
|
|
# the element and return its content instead |
91
|
10
|
100
|
|
|
|
22
|
return XML( @content ) if not $tag_name; |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
# Return the element start tag, with its formatted and |
94
|
|
|
|
|
|
|
# encoded attributes, and the content and end tag; or, |
95
|
|
|
|
|
|
|
# if no content, a self-closing empty element |
96
|
9
|
100
|
|
|
|
22
|
join '', '<', $tag_name, attributes( %$attributes ), |
97
|
|
|
|
|
|
|
@content ? ( '>', XML( @content ), "$tag_name>" ) : '/>' |
98
|
|
|
|
|
|
|
} |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=head2 start_tag() |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Takes a list with an element name and an optional hashref defining the element's attributes, and returns just the opening tag of the element. This and end_tag() are useful in those occasions when you really want to print out XML piecewise procedurally, rather than building the whole page in memory. |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
=cut |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub start_tag { |
107
|
3
|
|
|
3
|
1
|
30
|
my ( $tag_name, $attributes ) = @_; |
108
|
|
|
|
|
|
|
|
109
|
3
|
|
|
|
|
15
|
join '', grep $_, |
110
|
|
|
|
|
|
|
'<', $tag_name, attributes( %$attributes ), '>'; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
=head2 end_tag() |
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
Just takes an element name and returns the end tag for that element. |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
=cut |
118
|
|
|
|
|
|
|
|
119
|
1
|
|
|
1
|
1
|
4
|
sub end_tag { "$_[0]>" } |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
=head2 attributes() |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
Takes a hash of XML element attributes and returns an encoded string for use in a tag. |
124
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
=cut |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
sub attributes { |
128
|
|
|
|
|
|
|
|
129
|
15
|
100
|
|
15
|
1
|
69
|
return unless my @attributes = @_; |
130
|
|
|
|
|
|
|
|
131
|
8
|
|
|
|
|
13
|
my @html; |
132
|
8
|
|
|
|
|
34
|
while ( my ($name, $value) = splice @attributes, 0, 2 ) { |
133
|
14
|
100
|
|
|
|
410
|
if ( defined $value ) { |
134
|
8
|
|
|
|
|
31
|
push @html, join '', $name, '="', encode_entities( $value, '&<"' ), '"'; |
135
|
|
|
|
|
|
|
} |
136
|
|
|
|
|
|
|
} |
137
|
8
|
|
|
|
|
446
|
join ' ', '', @html; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=head2 XMLdecl() |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
This makes it easy to add a valid XML declaration to your document. |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
=cut |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
sub XMLdecl { |
147
|
3
|
|
|
3
|
1
|
13
|
my ( $version, $encoding, $standalone ) = @_; |
148
|
|
|
|
|
|
|
|
149
|
3
|
|
100
|
|
|
14
|
$version ||= '1.0'; |
150
|
|
|
|
|
|
|
|
151
|
3
|
|
|
|
|
8
|
join '', ' $version, encoding => $encoding, standalone => $standalone ), '?>'; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=head2 doctype() |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
This makes it easy to add a valid doctype declaration to your document. |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=cut |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
sub doctype { |
161
|
4
|
|
|
4
|
1
|
11
|
my ( $root, $URI, $pubID, $subset ) = @_; |
162
|
|
|
|
|
|
|
|
163
|
4
|
|
50
|
|
|
12
|
$root ||= 'XML'; |
164
|
4
|
|
66
|
|
|
21
|
$URI &&= uri_escape( $URI, '\x0-\x1F\x7F-\xFF <>"{}|\^``"' ); |
165
|
4
|
|
66
|
|
|
324
|
$URI &&= qq("$URI"); |
166
|
4
|
|
66
|
|
|
15
|
$pubID &&= qq("$pubID"); |
167
|
4
|
|
66
|
|
|
18
|
$subset &&= "[ $subset ]"; |
168
|
|
|
|
|
|
|
|
169
|
4
|
100
|
66
|
|
|
49
|
join( ' ', grep defined $_, |
170
|
|
|
|
|
|
|
'
|
171
|
|
|
|
|
|
|
$pubID ? ('PUBLIC', $pubID, $URI) : $URI && ('SYSTEM', $URI), |
172
|
|
|
|
|
|
|
$subset |
173
|
|
|
|
|
|
|
) . '>'; |
174
|
|
|
|
|
|
|
} |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
=head1 EXAMPLES |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
Note that I've formatted the output XML for clarity - the XML() function returns it all machine-readable and compact. |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
=head2 Simple content |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
Strings are just encoded and printed, so |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
print XML 'Hi there, this & that'; |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
would print |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
Hi there, this & that |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head2 Literal content |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
If an element's name is an arrayref, its first item is printed without being encoded; this lets you include text that is already encoded by double-bracketing it: |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
print XML [ copyright => [[ '© Angel Networks™' ]] ]; |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
would print |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
© Angel Networks™ |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
=head2 Using map to iterate, and optional elements |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
You can map any element over a list to iterate it, and by testing the value being mapped over can wrap some values in sub-elements: |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
print XML map [ number => [ $_ > 100 && large => $_ ] ], 4, 450, 12, 44, 74, 102; |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
would print |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
4 |
209
|
|
|
|
|
|
|
450 |
210
|
|
|
|
|
|
|
12 |
211
|
|
|
|
|
|
|
44 |
212
|
|
|
|
|
|
|
74 |
213
|
|
|
|
|
|
|
102 |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head2 Optional attributes |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Similarly, by testing the value being mapped over in the attributes hash, you can set an attribute for only some values. Note that you have to explicitly return undef to skip the attribute since 0 is a valid value for an attribute. |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
print XML [ states => |
220
|
|
|
|
|
|
|
map |
221
|
|
|
|
|
|
|
[ state => { selected => $_ eq $c{state} || undef }, $_ ], |
222
|
|
|
|
|
|
|
@states |
223
|
|
|
|
|
|
|
]; |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
would print |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Alabama |
229
|
|
|
|
|
|
|
Alaska |
230
|
|
|
|
|
|
|
Arkansas |
231
|
|
|
|
|
|
|
... |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
assuming $c{state} equalled 'Alaska'. |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=head2 Printing XML tags one at a time |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
Sometimes you really don't want to build the whole document before printing it; you'd rather loop through some data and print an element at a time. The start_tag and end_tag functions will help you do this: |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
print start_tag( [ document => { columns => 3 } ] ); |
241
|
|
|
|
|
|
|
print end_tag( 'document' ); |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
would print |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
|
248
|
|
|
|
|
|
|
=head2 XML declaration |
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
You can print an XML declaration with the XMLdecl() function. |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
print XMLdecl(); |
253
|
|
|
|
|
|
|
|
254
|
|
|
|
|
|
|
would print the default XML declaration |
255
|
|
|
|
|
|
|
|
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
but you can change the version and encoding by passing up to two arguments: |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
print XMLdecl('1.1', 'CP-1252'); |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
would print |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=head2 Doctyoe |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
The doctype() function can be called without arguments to print a default doctype: |
269
|
|
|
|
|
|
|
|
270
|
|
|
|
|
|
|
print doctype(); |
271
|
|
|
|
|
|
|
|
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
or with one argument to set the root element name: |
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
print doctype('html'); |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
The second argument, if defined, is a URI; if no third argument is given, then it's printed as a private SYSTEM URI: |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
print doctype('transaction', 'http://example.com/transaction.dtd'); |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
|
286
|
|
|
|
|
|
|
The third argument, if defined, is a public ID which will make the doctype public: |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
print doctype('HTML', 'http://www.w3.org/TR/html4/strict.dtd', '-//W3C//DTD HTML 4.01//EN'); |
289
|
|
|
|
|
|
|
|
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
|
292
|
|
|
|
|
|
|
Finally, if a fourth argument is given, it's a internal subset, which could contain markup declarations for entities, elements, &c. |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
print doctype('transaction', undef, undef, '' ); |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
]> |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
=head1 SEE ALSO |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
L |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=head1 AUTHOR |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
Nic Wolff, |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=head1 BUGS |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
Please report any bugs or feature requests through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head1 SUPPORT |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
perldoc XML::FromArrayref |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
You can also look for information at: |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
=over 4 |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item * This module on GitHub |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
L |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=item * GitHub request tracker (report bugs here) |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
L |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
L |
331
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
=item * Search CPAN |
333
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
L |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=back |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
Copyright 2013 Nic Wolff. |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
343
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
344
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
See http://dev.perl.org/licenses/ for more information. |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=cut |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
1; # End of XML::FromArrayref |