File Coverage

blib/lib/XML/FromArrayref.pm
Criterion Covered Total %
statement 46 46 100.0
branch 16 16 100.0
condition 16 22 72.7
subroutine 13 13 100.0
pod 7 7 100.0
total 98 104 94.2


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 ), "" ) : '/>'
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 { "" }
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