File Coverage

blib/lib/Catmandu/Fix/xpath_map.pm
Criterion Covered Total %
statement 42 42 100.0
branch 2 4 50.0
condition n/a
subroutine 8 8 100.0
pod 1 2 50.0
total 53 56 94.6


line stmt bran cond sub pod time code
1             package Catmandu::Fix::xpath_map;
2              
3 1     1   321975 use Catmandu::Sane;
  1         136009  
  1         6  
4 1     1   274 use XML::LibXML::XPathContext;
  1         2  
  1         22  
5 1     1   5 use Moo;
  1         1  
  1         5  
6 1     1   759 use Catmandu::Fix::Has;
  1         763  
  1         5  
7              
8             with 'Catmandu::Fix::Base';
9              
10             our $VERSION = '0.05';
11              
12             has old_path => (fix_arg => 1);
13             has xpath => (fix_arg => 1);
14             has new_path => (fix_arg => 1);
15             has namespaces => (fix_opt => 'collect');
16              
17             sub emit {
18 1     1 0 234341 my ($self, $fixer) = @_;
19              
20 1         7 my $old_path = $fixer->split_path($self->old_path);
21 1         41 my $old_key = pop @$old_path;
22 1         4 my $new_path = $fixer->split_path($self->new_path);
23 1         19 my $xpath = $fixer->capture($self->xpath);
24              
25 1         36 my $vals = $fixer->generate_var;
26 1         9 my $current_val = $fixer->generate_var;
27 1         7 my $perl = "";
28 1         6 $perl .= $fixer->emit_declare_vars($vals, '[]');
29 1         31 $perl .= $fixer->emit_declare_vars($current_val);
30              
31             $perl .= $fixer->emit_walk_path(
32             $fixer->var,
33             $old_path,
34             sub {
35 2     2   187 my $var = shift;
36             $fixer->emit_get_key(
37             $var, $old_key,
38             sub {
39 2         40 my $var = shift;
40 2         6 "push(\@{${vals}}, ${var});";
41             }
42 2         12 );
43             }
44 1         24 );
45              
46 1         19 my $my_self = $fixer->capture($self);
47              
48             $perl
49             .= "while (\@{${vals}}) {"
50             . "${current_val} = ${my_self}->xpath_map(shift(\@{${vals}}),${xpath});"
51             . $fixer->emit_create_path(
52             $fixer->var,
53             $new_path,
54             sub {
55 1     1   65 my $var = shift;
56 1         17 "${var} = ${current_val};";
57             }
58 1         52 ) . "}";
59              
60 1         14 $perl;
61             }
62              
63             sub xpath_map {
64 1     1 1 2774 my ($self, $data, $xpath) = @_;
65              
66 1 50       6 unless (ref($data) =~ /^XML::LibXML/) {
67 1         4 my ($key) = keys %$data;
68 1         4 $data = $data->{$key}->[0];
69             }
70              
71 1         44 my $xc = XML::LibXML::XPathContext->new($data);
72              
73 1 50       7 if ($self->namespaces) {
74 1         2 for (keys %{$self->namespaces}) {
  1         5  
75 1         16 $xc->registerNs($_,$self->namespaces->{$_});
76             }
77             }
78              
79 1         7 $xc->findvalue($xpath);
80             }
81              
82             1;
83              
84             __END__
85              
86             =pod
87              
88             =head1 NAME
89              
90             Catmandu::Fix::xpath_map - map values from a XML::LibXML::Element value to a field
91              
92             =head1 SYNOPSIS
93              
94             # <mets:dmdSec ID="dmd1">
95             # <mets:mdWrap MDTYPE="MODS">
96             # <mets:xmlData>
97             # <mods:mods xmlns:mods="http://www.loc.gov/mods/v3" ID="mods1" version="3.4">
98             # <mods:titleInfo>
99             # <mods:title>Alabama blues</mods:title>
100             # </mods:titleInfo>
101             # </mods:mods>
102             # </mets:xmlData>
103             # </mets:mdWrap>
104             # </mets:dmdSec>
105              
106             # The dmdSec.0.mdWrap.xmlData contains a XML::LibXML::Element
107             # Map the value of the 'mods:titleInfo/mods:title' XPath to
108             # a new field 'my_new_field'.
109             # Optionally provide one or more namespace mappings to use
110             xpath_map(
111             dmdSec.0.mdWrap.xmlData,
112             'mods:titleInfo/mods:title',
113             my_new_field,
114             -mods:'http://www.loc.gov/mods/v3'
115             )
116              
117             # Result:
118             #
119             # 'my_new_field' => 'Alabama blues'
120              
121             =head1 DESCRIPTION
122              
123             Not all XML fields in an XML Schema can be mapped to a Perl Hash using Catmandu::XSD.
124             Especially <any> fields in a schema, which can contain any type of XML are problematic.
125             These fields are mapped into a blessed XML::LibXML::Element object. Using the
126             C<xpath_map> Fix, on can access these blessed objects and extract data from it
127             using XPaths.
128              
129             =head1 METHOD
130              
131             =head2 xpath_map(xml_field, xpath, new_field [, namespace-prefix:namespace-url [,...]])
132              
133             Map an XML field at C<xml_field> to C<new_field> using an XPath expresssion C<xpath>.
134              
135             =head1 SEE ALSO
136              
137             L<Catmandu::Fix>
138              
139             =head1 AUTHOR
140              
141             Patrick Hochstenbach , C<< patrick.hochstenbach at ugent.be >>
142              
143             =head1 LICENSE AND COPYRIGHT
144              
145             This program is free software; you can redistribute it and/or modify it
146             under the terms of either: the GNU General Public License as published
147             by the Free Software Foundation; or the Artistic License.
148              
149             See L<http://dev.perl.org/licenses/> for more information.
150              
151             =cut