File Coverage

blib/lib/Catmandu/Importer/XSD.pm
Criterion Covered Total %
statement 46 55 83.6
branch 7 12 58.3
condition n/a
subroutine 12 14 85.7
pod 0 3 0.0
total 65 84 77.3


line stmt bran cond sub pod time code
1             package Catmandu::Importer::XSD;
2              
3 1     1   360296 use Catmandu::Sane;
  1         9  
  1         8  
4 1     1   204 use Catmandu::Util qw(:is);
  1         2  
  1         241  
5 1     1   384 use XML::LibXML::Reader;
  1         1507  
  1         87  
6 1     1   342 use Catmandu::XSD;
  1         4  
  1         35  
7 1     1   6 use feature 'state';
  1         2  
  1         72  
8              
9             our $VERSION = '0.05';
10              
11 1     1   6 use Moo;
  1         2  
  1         5  
12 1     1   415 use namespace::clean;
  1         2  
  1         8  
13              
14             with 'Catmandu::Importer';
15              
16             has 'root' => (is => 'ro' , required => 1);
17             has 'schemas' => (is => 'ro' , required => 1);
18             has 'mixed' => (is => 'ro' , default => sub { 'ATTRIBUTES' });
19             has 'any_element' => (is => 'ro' , default => sub { 'TAKE_ALL' });
20             has 'prefixes' => (is => 'ro' , default => sub { [] });
21             has 'files' => (is => 'ro');
22             has 'xpath' => (is => 'ro' , default => sub { '*' });
23             has 'example' => (is => 'ro');
24              
25             has 'xsd' => (is => 'lazy');
26              
27             sub _build_xsd {
28 2     2   20 my $self = $_[0];
29 2         42 return Catmandu::XSD->new(
30             root => $self->root ,
31             schemas => $self->schemas ,
32             mixed => $self->mixed ,
33             any_element => $self->any_element ,
34             prefixes => $self->prefixes ,
35             );
36             }
37              
38             sub generator {
39             my $self = $_[0];
40              
41             if ($self->example) {
42             $self->example_generator
43             }
44             elsif ($self->files) {
45             $self->multi_file_generator
46             }
47             else {
48             $self->single_file_generator
49             }
50             }
51              
52             sub example_generator {
53 0     0 0 0 my $self = $_[0];
54              
55 0         0 my $count = 0;
56              
57             sub {
58 0 0   0   0 $count++ ? undef : $self->xsd->template;
59 0         0 };
60             }
61              
62             sub multi_file_generator {
63 1     1 0 1 my $self = $_[0];
64              
65 1         108 my @files = glob($self->files);
66              
67             sub {
68 3     3   99 my $file = shift @files;
69              
70 3 100       10 return undef unless $file;
71 2         13 my $xml = XML::LibXML->load_xml(location => $file);
72 2         1015 $self->xsd->parse($xml);
73 1         10 };
74             }
75              
76             sub single_file_generator {
77 1     1 0 4 my $self = $_[0];
78              
79 1         2 my $prefixes = {};
80              
81 1 50       6 if ($self->prefixes) {
82 1 50       6 if (is_array_ref $self->prefixes) {
83 1         2 for (@{$self->prefixes}) {
  1         4  
84 0         0 my ($key,$val) = each %$_;
85 0         0 $prefixes->{$key} = $val;
86             }
87             }
88             else {
89 0         0 for (split(/,/,$self->prefixes)) {
90 0         0 my ($key,$val) = split(/:/,$_,2);
91 0         0 $prefixes->{$key} = $val;
92             }
93             }
94             }
95              
96             # Drop all PerlIO layers possibly created by a use open pragma
97             # requirement for XML::LibXML parsing
98             # See: https://metacpan.org/pod/distribution/XML-LibXML/LibXML.pod
99 1         19 binmode $self->fh;
100              
101             sub {
102 3     3   44 state $reader = XML::LibXML::Reader->new(IO => $self->fh);
103              
104 3         169 my $match = $reader->nextPatternMatch(
105             XML::LibXML::Pattern->new($self->xpath , $prefixes)
106             );
107              
108 3 100       182 return undef unless $match == 1;
109              
110 2         81 my $xml = $reader->readOuterXml();
111              
112 2 50       65 return undef unless length $xml;
113              
114 2         26 $reader->nextSibling();
115              
116 2         51 my $data = $self->xsd->parse($xml);
117              
118 2         52 return $data;
119 1         275 };
120             }
121              
122             1;
123              
124             __END__
125              
126             =pod
127              
128             =head1 NAME
129              
130             Catmandu::Importer::XSD - Import and validate serialized XML documents
131              
132             =head1 SYNOPSIS
133              
134             # Compile an XSD schema file and parse one shiporder.xml file
135             catmandu convert XSD --root '{}shiporder'
136             --schemas demo/order/*.xsd
137             to YAML < shiporder.xml
138              
139             # Same as above but parse more than one file into an array of records
140             catmandu convert XSD --root '{}shiporder'
141             --schemas demo/order/*.xsd
142             --files 'data/*.xml'
143             to YAML
144              
145             # Same as above but all array of records are in a XML container file
146             catmandu convert XSD --root '{}shiporder'
147             --schemas demo/order/*.xsd
148             --xpath '/Container/List//Record/Payload/*'
149             to YAML < data/container.xml
150              
151             # In Perl
152             use Catmandu;
153              
154             my $importer = Catmandu->importer('XSD',
155             file => 'ex/data.xml'
156             root => ...,
157             schemas => [ ...]
158             );
159              
160             my $n = $importer->each(sub {
161             my $hashref = $_[0];
162             # ...
163             });
164              
165             =head1 DESCRIPTION
166              
167             This is a L<Catmandu::Importer> for parsing and validating XML data using one or
168             more XSD schema files.
169              
170             =head1 CONFIGURATION
171              
172             =over
173              
174             =item file
175              
176             Read input from a local file given by its path. Alternatively a scalar
177             reference can be passed to read from a string.
178              
179             =item fh
180              
181             Read input from an L<IO::Handle>. If not specified, L<Catmandu::Util::io> is used to
182             create the input stream from the C<file> argument or by using STDIN.
183              
184             =item files
185              
186             Optional. Don't read the content from the standard input but use the 'files' parameter
187             as a glob for one or more filenames. E.g.
188              
189             catmandu ... --files 'data/input/*.xml'
190              
191             =item examples
192              
193             Optional. Don't do anything only show an example output how a document should be
194             structured in the given XSD scheme. E.g.
195              
196             catmandu convert XSD --root {}shiporder --schemas "t/demo/ead/*xsd" --example 1 to YAML
197              
198             =item fix
199              
200             An ARRAY of one or more fixes or file scripts to be applied to imported items.
201              
202             =item root
203              
204             Required. The name (and namespace) of the root element of the XML document. E.g.:
205              
206             {}shiporder
207             {http://www.loc.gov/mods/v3}mods
208             {urn:isbn:1-931666-22-9}ead
209              
210             =item schemas
211              
212             Required. An array or comma separated list of XSD schema locations.
213              
214             =item xpath
215              
216             Optional. An XPath expression, the XML container in which the PNX record can
217             be found. Default : /oai:OAI-PMH/oai:ListRecords//oai:record/oai:metadata/*
218              
219             =item prefixes
220              
221             Optional. An array or comma delimited string of namespace prefixes to be used
222             hand handling XML files. E.g.
223              
224             # On the command line:
225             catmandu ... --prefixes ead:urn:isbn:1-931666-22-9,...
226              
227             # In Perl
228             prefixes => [
229             ead => 'urn:isbn:1-931666-22-9' ,
230             ... => ...
231             ]
232              
233             =item mixed
234              
235             Optional. The handling of mixed element content. One of ATTRIBUTES (default),
236             TEXTUAL, STRUCTURAL, XML_NODE, XML_STRING, CODE reference. See also
237             L<Catmandu::XSD> and L<XML::Compile::Translate::Reader>
238              
239             =item any_element
240              
241             Optional. The handling of C<<any>> content in schemas. One of TAKE_ALL (default:
242             process as XML::LibXML::Node) , SKIP_ALL (ignore these) , XML_STRING (process as string)
243             , CODE (provide a reference to parse the data). See L<XML::Compile::Translate::Reader>
244              
245             =back
246              
247             =head1 METHODS
248              
249             Every L<Catmandu::Importer> is a L<Catmandu::Iterable> all its methods are
250             inherited.
251              
252             =head1 SEE ALSO
253              
254             L<Catmandu::Importer>, L<Catmandu::XSD>
255              
256             =head1 AUTHOR
257              
258             Patrick Hochstenbach , C<< patrick.hochstenbach at ugent.be >>
259              
260             =head1 LICENSE AND COPYRIGHT
261              
262             This program is free software; you can redistribute it and/or modify it
263             under the terms of either: the GNU General Public License as published
264             by the Free Software Foundation; or the Artistic License.
265              
266             See L<http://dev.perl.org/licenses/> for more information.
267              
268             =cut