File Coverage

blib/lib/XML/Quick.pm
Criterion Covered Total %
statement 56 60 93.3
branch 28 38 73.6
condition 8 10 80.0
subroutine 7 7 100.0
pod 1 1 100.0
total 100 116 86.2


line stmt bran cond sub pod time code
1             package XML::Quick;
2              
3 3     3   72423 use warnings;
  3         9  
  3         105  
4 3     3   20 use strict;
  3         7  
  3         120  
5              
6 3     3   17 use Scalar::Util qw(reftype);
  3         9  
  3         277  
7 3     3   17 use Exporter;
  3         6  
  3         152  
8              
9 3     3   19 use base qw(Exporter);
  3         6  
  3         2684  
10              
11             our @EXPORT = qw(xml);
12              
13             our $VERSION = '0.05';
14              
15             # cdata escaping
16             sub _escape($) {
17 11     11   13 my ($cdata) = @_;
18              
19 11         20 $cdata =~ s/&/&/g;
20 11         17 $cdata =~ s/
21 11         17 $cdata =~ s/>/>/g;
22 11         12 $cdata =~ s/"/"/g;
23              
24 11         18 $cdata =~ s/([^\x20-\x7E])/'&#' . ord($1) . ';'/ge;
  0         0  
25              
26 11         25 return $cdata;
27             };
28              
29             sub xml {
30 16     16 1 4756 my ($data, $opts) = @_;
31              
32             # handle undef properly
33 16 50       38 $data = '' if not defined $data;
34            
35 16 100 66     60 if (not defined $opts or reftype $opts ne 'HASH') {
36             # empty options hash if they didn't provide one
37 10         16 $opts = {};
38             }
39             else {
40             # shallow copy the opts so we don't modify the callers
41 6         17 $opts = {%$opts};
42             }
43              
44             # escape by default
45 16 50       64 $opts->{escape} = 1 if not exists $opts->{escape};
46              
47 16         20 my $xml = '';
48              
49             # stringify anything thats not a hash
50 16 100 66     109 if(not defined reftype $data or reftype $data ne 'HASH') {
51 10 50       34 $xml = $opts->{escape} ? _escape($data) : $data;
52             }
53              
54             # dig down into hashes
55             else {
56             # move attrs/cdata into opts as necessary
57 6 100       16 if(exists $data->{_attrs}) {
58 2 50       7 $opts->{attrs} = $data->{_attrs} if not exists $opts->{attrs};
59             }
60              
61 6 100       13 if(exists $data->{_cdata}) {
62 1 50       4 $opts->{cdata} = $data->{_cdata} if not exists $opts->{cdata};
63             }
64            
65             # loop over the keys
66 6         9 for my $key (keys %{$data}) {
  6         14  
67             # skip meta
68 8 100       30 next if $key =~ m/^_/;
69              
70             # undef
71 5 50       23 if(not defined $data->{$key}) {
    100          
    50          
    0          
72 0         0 $xml .= xml('', { root => $key });
73             }
74              
75             # plain scalar
76             elsif(not ref $data->{$key}) {
77 2         9 $xml .= xml($data->{$key}, { root => $key });
78             }
79              
80             # hash
81             elsif(reftype $data->{$key} eq 'HASH') {
82 3   100     32 $xml .= xml($data->{$key}, { root => $key,
      100        
83             attrs => $data->{$key}->{_attrs} || {},
84             cdata => $data->{$key}->{_cdata} || '' })
85             }
86              
87             # array
88             elsif(reftype $data->{$key} eq 'ARRAY') {
89 0         0 $xml .= xml($_, { root => $key }) for @{$data->{$key}};
  0         0  
90             }
91             }
92             }
93              
94             # wrap it up
95 16 100       41 if($opts->{root}) {
96             # open the tag
97 5         9 my $wrap = "<$opts->{root}";
98              
99             # attribute list
100 5 100       12 if($opts->{attrs}) {
101 3         5 for my $key (keys %{$opts->{attrs}}) {
  3         9  
102 2         4 my $val = $opts->{attrs}->{$key};
103 2         4 $val =~ s/'/'/;
104              
105 2         8 $wrap .= " $key='$opts->{attrs}->{$key}'";
106             }
107             }
108              
109             # character data
110 5 100       13 if($opts->{cdata}) {
111 1 50       17 $xml = ($opts->{escape} ? _escape($opts->{cdata}) : $opts->{cdata}) . $xml;
112             }
113              
114             # if there's no content, then close it up right now
115 5 100       11 if($xml eq '') {
116 2         3 $wrap .= '/>';
117             }
118              
119             # otherwise dump in the contents and close
120             else {
121 3         9 $wrap .= ">$xml{root}>";
122             }
123              
124 5         8 $xml = $wrap;
125             }
126              
127             # all done
128 16         61 return $xml;
129             }
130              
131             1;
132              
133             __END__