File Coverage

blib/lib/WWW/Splunk/XMLParser.pm
Criterion Covered Total %
statement 38 38 100.0
branch 28 28 100.0
condition 23 24 95.8
subroutine 6 6 100.0
pod 2 2 100.0
total 97 98 98.9


line stmt bran cond sub pod time code
1             =encoding utf8
2              
3             =head1 NAME
4              
5             WWW::Splunk::XMLParser - Parse Splunk XML format
6              
7             =head1 DESCRIPTION
8              
9             This is an utility module to deal with XML format ocassionally returned
10             by Splunk and seemlingly undocumented.
11              
12             Note that Splunk usually returns Atom XMLs, which have the same
13             content type. They can be distinguished by a DOCTYPE.
14              
15             =cut
16              
17             package WWW::Splunk::XMLParser;
18              
19 7     7   106883 use strict;
  7         11  
  7         211  
20 7     7   34 use warnings;
  7         12  
  7         301  
21              
22 7     7   4726 use XML::LibXML qw/:libxml/;
  7         418686  
  7         47  
23 7     7   1342 use Carp;
  7         13  
  7         3889  
24              
25             our $VERSION = '2.09';
26              
27             =head2 B (F)
28              
29             Return a perl structure from a XML string, if it's
30             parsable, otherwise return a raw XML::LibXML object
31              
32             =cut
33              
34             sub parse {
35 5     5 1 234789 my $xml = shift;
36              
37 5         8 my @tree = eval { parsetree ($xml) };
  5         9  
38 5 100       139 return $xml if $@;
39 3 100       43 return $#tree ? @tree : $tree[0];
40             }
41              
42             =head2 B (F)
43              
44             Parse a XML node tree recursively.
45              
46             =cut
47              
48             sub parsetree {
49 53     53 1 226 my $xml = shift;
50 53         48 my @retval;
51              
52 53         84 my $has_elements = grep { $_->nodeType eq XML_ELEMENT_NODE }
  71         399  
53             $xml->nonBlankChildNodes ();
54              
55 53         108 foreach my $node ($xml->nonBlankChildNodes ()) {
56              
57             # Not interested in anything but elements
58 71 100 100     786 next if $has_elements and $node->nodeType ne XML_ELEMENT_NODE;
59              
60             # Structure or structure wrapped in Atom
61 68 100 100     733 if ($node->nodeName () eq 'list' or
    100 100        
    100 100        
    100 100        
    100 100        
    100 66        
    100 100        
    100          
    100          
62             $node->nodeName () eq 's:list') {
63 4         7 push @retval, [ parsetree ($node) ];
64             } elsif ($node->nodeName () eq 'dict' or
65             $node->nodeName () eq 's:dict') {
66 9         27 push @retval, { parsetree ($node) };
67             } elsif ($node->nodeName () eq 'key' or
68             $node->nodeName () eq 's:key') {
69 20         28 push @retval, $node->getAttribute ('name')
70             => scalar parsetree($node);
71             } elsif ($node->nodeName () eq 'response' or
72             $node->nodeName () eq 'item' or
73             $node->nodeName () eq 's:item') {
74             # Basically just ignore these
75 7         17 push @retval, parsetree ($node);
76             } elsif ($node->nodeName () eq 'entry') {
77             # Crippled Atom envelope
78 1         4 foreach my $node ($node->childNodes ()) {
79 18 100       44 return parsetree ($node) if $node->nodeName () eq 'content';
80             }
81             } elsif ($node->nodeType eq XML_TEXT_NODE or $node->nodeName () eq '#cdata-section') {
82 14         68 return $node->textContent;
83              
84             # Results
85             } elsif ($node->nodeName () eq 'results') {
86 2         21 return map { { parsetree ($_) } }
87 1         2 grep { $_->nodeName eq 'result' }
  7         20  
88             $node->childNodes;
89             } elsif ($node->nodeName () eq 'field') {
90 5         25 push @retval, $node->getAttribute ('k')
91             => scalar parsetree($node);
92             } elsif ($node->nodeName () eq 'value'
93             or $node->nodeName () eq 'v') {
94 5         44 return $node->textContent;
95              
96             # Errors
97             } else {
98 2         18 die "Unknown XML element: ".$node->nodeName
99             }
100             }
101              
102 28 100       231 return wantarray ? @retval : $retval[0];
103             }
104              
105             =head1 SEE ALSO
106              
107             L, L, L
108              
109             =head1 AUTHORS
110              
111             Lubomir Rintel, L<< >>,
112             Michal Josef Špaček L<< >>
113              
114             The code is hosted on GitHub L.
115             Bug fixes and feature enhancements are always welcome.
116              
117             =head1 LICENSE
118              
119             This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
120              
121             =cut
122              
123             1;