1 use strict; use warnings;
2 package OpenILS::Utils::FlatXML;
5 my $_tC_mask = 1 << XML_TEXT_NODE | 1 << XML_COMMENT_NODE | 1 << XML_CDATA_SECTION_NODE | 1 << XML_DTD_NODE;
6 my $_val_mask = 1 << XML_ATTRIBUTE_NODE | 1 << XML_NAMESPACE_DECL;
9 my $parser = XML::LibXML->new();
10 $parser->keep_blanks(0);
11 sub new { return bless({},shift()); }
15 return $parser->parse_string( $xml );
20 return $self->_xml_to_nodeset( $self->xml_to_doc( $xml ) );
23 sub xmlfile_to_nodeset {
24 my($self, $xmlfile) = @_;
25 return $self->_xml_to_nodeset( $self->xmlfile_to_doc( $xmlfile ) );
29 my($self, $xmlfile) = @_;
30 return $parser->parse_file( $xmlfile );
37 my $doc = XML::LibXML::Document->new;
42 for my $node ( @{ $$nodeset{doclist} } ) {
45 if ( $node->{type} == XML_ELEMENT_NODE ) {
47 $xml = $doc->createElement( $node->{name} );
50 my ($ns) = grep { $node->{ns} eq $_->{uri} } @{ $$nodeset{nslist} };
51 $xml->setNamespace($ns->{uri}, $ns->{prefix}, 1) if ($ns->{prefix} || !$seen_ns{$ns->{uri}});
52 $seen_ns{$ns->{uri}}++;
55 } elsif ( $node->{type} == XML_TEXT_NODE ) {
56 $xml = $doc->createTextNode( $node->{value} );
58 } elsif ( $node->{type} == XML_COMMENT_NODE ) {
59 $xml = $doc->createComment( $node->{value} );
61 } elsif ( $node->{type} == XML_ATTRIBUTE_NODE ) {
64 my ($ns) = grep { $node->{ns} eq $_->{uri} } @{ $$nodeset{nslist} };
65 $_xmllist[$node->{parent}]->setAttributeNS($ns->{uri}, $node->{name}, $node->{value});
67 $_xmllist[$node->{parent}]->setAttribute($node->{name}, $node->{value});
73 $_xmllist[$node->{id}] = $xml;
75 if (defined $node->{parent}) {
76 $_xmllist[$node->{parent}]->addChild($xml);
80 $doc->setDocumentElement($_xmllist[0]);
85 # --------------------------------------------------------------
86 # -- Builds a list of nodes from a given xml doc
94 return undef unless($doc);
95 my $node = $doc->documentElement;
96 return undef unless($node);
98 _grab_namespaces($node);
103 name => $node->localname,
105 type => $node->nodeType,
106 ns => $node->namespaceURI
109 $self->_nodeset_recurse( $node, 0);
111 # clear out the global variables
113 my @tmpnslist = @nslist;
118 return { doclist => [@tmp], nslist => [@tmpnslist] };
121 sub _grab_namespaces {
123 # add to the ns list if not alread there
124 for my $ns ($node->getNamespaces) {
125 if (my ($existing_ns) = grep { $_->{uri} eq $ns->value } @nslist) {
126 $existing_ns->{prefix} = $ns->localname;
129 push @nslist, { prefix => $ns->localname, uri => $ns->value };
133 sub _nodeset_recurse {
135 my( $self, $node, $parent) = @_;
136 return undef unless($node && $node->nodeType == 1);
138 _grab_namespaces($node);
140 for my $kid ( ($node->attributes, $node->childNodes) ) {
141 next if ($kid->nodeType == 18);
143 my $type = $kid->nodeType;
148 name => $kid->localname,
149 value => _grab_content( $kid, $type ),
151 ns => $kid->namespaceURI
154 return if ($type == 3);
155 $self->_nodeset_recurse( $kid, $next_id);
161 my $type = 1 << shift();
163 return undef if ($type & 1 << XML_ELEMENT_NODE);
164 return $node->textContent if ($type & $_tC_mask);
165 return $node->value if ($type & $_val_mask);
166 return $node->getData if ($type & 1 << XML_PI_NODE);