File Coverage

blib/lib/XML/GenericJSON.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package XML::GenericJSON;
2 2     2   65661 use Exporter;
  2         5  
  2         198  
3              
4             @ISA = ('Exporter');
5             @EXPORT = qw();
6             our $VERSION = 0.05;
7              
8 2     2   12 use strict;
  2         4  
  2         75  
9 2     2   12 use warnings;
  2         7  
  2         79  
10              
11 2     2   1003 use XML::LibXML;
  0            
  0            
12             use JSON::XS;
13              
14             =head1 NAME
15              
16             XML::GenericJSON - for turning XML into JSON, preserving as much XMLness as possible.
17              
18             =head1 SYNOPSIS
19              
20             my $json_string = XML::GenericJSON::string2string($xml_string);
21              
22             my $json_string = XML::GenericJSON::file2string($xml_filename);
23              
24             XML::GenericJSON::string2file($xml_string,$json_filename);
25              
26             XML::GenericJSON::file2file($xml_filename,$json_filename);
27              
28             =head1 DESCRIPTION
29              
30             XML::GenericJSON provides functions for turning XML into JSON. It uses LibXML to parse
31             the XML and JSON::XS to turn a perlish data structure into JSON. The perlish data structure
32             preserves as much XML information as possible. (In other words, an application-specific JSON filter
33             would almost certainly produce more compact JSON.)
34              
35             The module was initially developed as part of the Xcruciate project (F)
36             to produce JSON output via the Xteriorize webserver. It turns the entire XML document into a DOM tree,
37             which may not be what you want to do if your XML document is 3 buzillion lines long.
38              
39             =head1 AUTHOR
40              
41             Mark Howe, Emelonman@cpan.orgE
42              
43             =head2 EXPORT
44              
45             None
46              
47             =head1 BUGS
48              
49             The best way to report bugs is via the Xcruciate bugzilla site (F).
50              
51             =head1 PREVIOUS VERSIONS
52              
53             =cut
54              
55             my @types=(0,
56             'element', #1
57             'attribute', #2
58             'text', #3
59             'cdata', #4
60             'entity_ref', #5
61             'entity_node', #6
62             'pi', #7
63             'comment', #8
64             'document', #9
65             'document_type', #10
66             'document_frag', #11
67             'notation', #12
68             'html_document', #13
69             'dtd', #14
70             'element_decl', #15
71             'attribute_decl', #16
72             'entity_decl', #17
73             'namespace_decl', #18
74             'xinclude_start', #19
75             'xinclude_end', #20
76             'docb_document' #21
77             );
78              
79             my $simple_types = {4=>1,
80             7=>1,
81             8=>1};
82              
83             my $xml_parser = new XML::LibXML;
84              
85             =head2 string2string(xml_string [,preserve_whitespace])
86              
87             Returns a JSON representation of an XML string. The second argument should be false if you want to preserve non-semantic whitespace.
88              
89             =cut
90              
91             sub string2string {
92             my $xml_string = shift;
93             my $strip_whitespace = 1;
94             $strip_whitespace = shift if defined $_[0];
95             my $dom = $xml_parser->parse_string($xml_string);
96             return (encode_json dom2perlish($dom->getDocumentElement,$strip_whitespace));
97             }
98              
99             =head2 file2string(xml_filename [,preserve_whitespace])
100              
101             Returns a JSON representation of an XML file. The second argument should be false if you want to preserve non-semantic whitespace.
102              
103             =cut
104              
105             sub file2string {
106             my $xml_filename = shift;
107             my $strip_whitespace = 1;
108             $strip_whitespace = shift if defined $_[0];
109             my $dom = $xml_parser->parse_file($xml_filename);
110             return (encode_json dom2perlish($dom->getDocumentElement,$strip_whitespace));
111             }
112              
113             =head2 string2file(xml_string, json_filename [,preserve_whitespace])
114              
115             Writes a JSON file based on an XML string. The third argument should be false if you want to preserve non-semantic whitespace.
116              
117             =cut
118              
119             sub string2file {
120             my $xml_string = shift;
121             my $json_filename = shift;
122             my $strip_whitespace = 1;
123             $strip_whitespace = shift if defined $_[0];
124             my $dom = $xml_parser->parse_string($xml_string);
125             my $json = (encode_json dom2perlish($dom->getDocumentElement,$strip_whitespace));
126             open(OUT,">$json_filename") or die "Could not write JSON to '$json_filename' :$!";
127             print OUT $json;
128             close OUT;
129             }
130              
131             =head2 file2file(xml_filename, json_filename [,preserve_whitespace])
132              
133             Writes a JSON file based on an XML file. The third argument should be false if you want to preserve non-semantic whitespace.
134              
135             =cut
136              
137             sub file2file {
138             my $xml_filename = shift;
139             my $json_filename = shift;
140             my $strip_whitespace = 1;
141             $strip_whitespace = shift if defined $_[0];
142             my $dom = $xml_parser->parse_file($xml_filename);
143             my $json = (encode_json dom2perlish($dom->getDocumentElement,$strip_whitespace));
144             open(OUT,">$json_filename") or die "Could not write JSON to '$json_filename' :$!";
145             print OUT $json;
146             close OUT;
147             }
148              
149             =head2 dom2perlish(node)
150              
151             The function that does the work of turning XML into a perlish data structure suitable for treatment by JSON::XS.
152              
153             =cut
154              
155             sub dom2perlish {
156             my $xml_node = shift;
157             my $strip_whitespace = 1;
158             $strip_whitespace = shift if defined $_[0];
159             my $perlish_node = {};
160             if ($xml_node->nodeType == 3) {#text - just store the scalar
161             return $xml_node->data;
162             }elsif (defined $simple_types->{$xml_node->nodeType}) {#cdata,pi,comment - store type plus data
163             $perlish_node->{type} = $types[$xml_node->nodeType];
164             $perlish_node->{data} = $xml_node->nodeValue;
165             return $perlish_node;
166             } else {#Probably an element, but it should work regardless
167             $perlish_node->{type} = $types[$xml_node->nodeType];
168             $perlish_node->{namespaces} = list_namespaces($xml_node) if $xml_node->getNamespaces;
169             $perlish_node->{prefix} = $xml_node->prefix if $xml_node->prefix;
170             $perlish_node->{name} = $xml_node->localname;
171             $perlish_node->{attributes} = hash_attributes($xml_node) if $xml_node->hasAttributes;
172             $perlish_node->{children} = list_children($xml_node,$strip_whitespace) if $xml_node->hasChildNodes;
173             return $perlish_node
174             }
175             }
176              
177             =head2 hash_attributes(node)
178              
179             Makes a hash of attributes.
180              
181             =cut
182              
183             sub hash_attributes {
184             my $xml_node = shift;
185             my $hash = {};
186             foreach ($xml_node->attributes) {
187             $hash->{$_->name}=$_->value}
188             return $hash;
189             }
190              
191             =head2 list_namespaces($node)
192              
193             Makes a list of namespaces.
194              
195             =cut
196              
197             sub list_namespaces {
198             my $xml_node = shift;
199             my @namespaces_list = ();
200             foreach ($xml_node->namespaces) {
201             my $namespace_hash={};
202             $namespace_hash->{prefix} = $_->getLocalName;
203             $namespace_hash->{uri} = $_->getData;
204             push @namespaces_list,$namespace_hash;
205             }
206             return [@namespaces_list];
207             }
208              
209             =head2 list_children(node)
210              
211             Makes a list of child nodes.
212              
213             =cut
214              
215             sub list_children {
216             my $xml_node = shift;
217             my $strip_whitespace = shift;
218             my @children = ();
219             foreach ($xml_node->childNodes) {
220             next if $strip_whitespace and ($_->nodeType == 3) and ($_->textContent=~/^\s*$/s);
221             push @children,dom2perlish($_,$strip_whitespace);
222             }
223             return [@children];
224             }
225              
226             =head1 PREVIOUS VERSIONS
227              
228             =over
229              
230             B<0.01>: First upload
231              
232             B<0.02>: Get dependencies right
233              
234             B<0.03>: Get path to abstract right
235              
236             B<0.04>: ported to use Module::Build
237              
238             B<0.05>: fixed unit test
239              
240             =back
241              
242             =head1 COPYRIGHT AND LICENSE
243              
244             Copyright (C) 2009 by SARL Cyberporte/Menteith Consulting
245              
246             This library is distributed under BSD licence (F).
247              
248             =cut
249              
250             1;