File Coverage

blib/lib/RPC/XML/Parser/LibXML.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             package RPC::XML::Parser::LibXML;
2 2     2   46104 use strict;
  2         4  
  2         72  
3 2     2   9 use warnings;
  2         4  
  2         49  
4 2     2   45 use 5.00800;
  2         11  
  2         131  
5             our $VERSION = '0.08';
6 2     2   22 use base qw/Exporter/;
  2         2  
  2         203  
7 2     2   917 use RPC::XML;
  0            
  0            
8             use XML::LibXML;
9             use MIME::Base64 ();
10             use Carp ();
11              
12             our @EXPORT = qw/parse_rpc_xml/;
13              
14             our $TYPE_MAP = +{
15             int => 'RPC::XML::int',
16             i4 => 'RPC::XML::int',
17             boolean => 'RPC::XML::boolean',
18             string => 'RPC::XML::string',
19             double => 'RPC::XML::double',
20             'dateTime.iso8601' => 'RPC::XML::datetime_iso8601',
21             };
22              
23             my $value_xpath = join "|", map "./$_", qw( int i4 boolean string double dateTime.iso8601 base64 struct array );
24              
25             sub parse_rpc_xml {
26             my $xml = shift;
27              
28             my $x = XML::LibXML->new({
29             no_network => 1,
30             expand_xinclude => 0,
31             expand_entities => 1,
32             load_ext_dtd => 0,
33             ext_ent_handler => sub { warn "External entities disabled."; '' },
34             });
35             my $doc = $x->parse_string($xml)->documentElement;
36              
37             if ($doc->findnodes('/methodCall')) {
38             return RPC::XML::request->new(
39             $doc->findvalue('/methodCall/methodName'),
40             _extract_values($doc->findnodes('//params/param/value'))
41             );
42             } elsif ($doc->findnodes('/methodResponse/params')) {
43             return RPC::XML::response->new(
44             _extract_values($doc->findnodes('//params/param/value'))
45             );
46             } elsif ($doc->findnodes('/methodResponse/fault')) {
47             return RPC::XML::response->new(
48             RPC::XML::fault->new(
49             $doc->findvalue('/methodResponse/fault/value/struct/member/value/int'),
50             $doc->findvalue('/methodResponse/fault/value/struct/member/value/string'),
51             ),
52             );
53             } else {
54             Carp::croak("invalid xml: $xml");
55             }
56             }
57              
58              
59             sub _extract_values {
60             my @value_nodes = @_;
61              
62             my @values;
63             for my $node (grep defined, @value_nodes) {
64             my($v_node) = $node->findnodes($value_xpath);
65             my $value;
66             if (defined $v_node) {
67             $value = _extract($v_node);
68             } else {
69             # foo is treated as by default
70             $value = RPC::XML::string->new($node->textContent);
71             }
72              
73             push @values, $value;
74             }
75              
76             return @values;
77             }
78              
79             sub _extract {
80             my $node = shift;
81              
82             return unless defined $node;
83              
84             my $nodename = $node->nodeName;
85             my $val = $node->textContent;
86             if ($nodename eq 'base64') {
87             return RPC::XML::base64->new(MIME::Base64::decode_base64($val));
88             } elsif ($nodename eq 'struct') {
89             my @members = $node->findnodes('./member'); # XXX
90             my $result = {};
91             for my $member (@members) {
92             my($name) = $member->findnodes('./name');
93             my($value) = _extract_values ($member->findnodes('./value') );
94             ($result->{$name->textContent}, ) = $value;
95             }
96             return RPC::XML::struct->new($result);
97             } elsif ($nodename eq 'array') {
98             return RPC::XML::array->new(_extract_values($node->findnodes($node->nodePath . '/data/value')));
99             } else {
100             my $class = $TYPE_MAP->{ $nodename } or return;
101             return $class->new($val);
102             }
103             }
104              
105             1;
106             __END__