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()); }
26 $self->{xmlfile} = $xml;
28 return $self->{xmlfile};
33 my $doc = $self->{doc};
35 $xml ||= $self->{xml};
36 $doc = $self->{doc} = $parser->parse_string( $xml );
44 $self->xml_to_doc( $xml );
45 my $nodeset = $self->_xml_to_nodeset;
51 return $self->{nodelist};
54 sub xmlfile_to_nodeset {
55 my($self, $xmlfile) = @_;
56 $self->xmlfile( $xmlfile );
57 $self->xmlfile_to_doc;
58 return $self->xml_to_nodeset;
67 my($self, $xmlfile) = @_;
68 $xmlfile ||= $self->xmlfile;
69 my $doc = $self->{doc};
71 $doc = $self->{doc} = $parser->parse_file( $xmlfile );
79 $nodeset ||= $self->nodeset;
81 my $doc = XML::LibXML::Document->new;
86 for my $node ( @$nodeset ) {
89 if ( $node->{type} == XML_ELEMENT_NODE ) {
91 $xml = $doc->createElement( $node->{name} );
93 $xml->setNodeName($seen_ns{$node->{ns}} . ':' . $xml->nodeName) if ($node->{ns} and $seen_ns{$node->{ns}});
95 } elsif ( $node->{type} == XML_TEXT_NODE ) {
96 $xml = $doc->createTextNode( $node->{value} );
98 } elsif ( $node->{type} == XML_COMMENT_NODE ) {
99 $xml = $doc->createComment( $node->{value} );
101 } elsif ( $node->{type} == XML_NAMESPACE_DECL ) {
102 if ($self->nodeset->[$node->{parent}]->{ns} eq $node->{value}) {
103 $_xmllist[$node->{parent}]->setNamespace($node->{value}, $node->{name}, 1);
105 $_xmllist[$node->{parent}]->setNamespace($node->{value}, $node->{name}, 0);
107 $seen_ns{$node->{value}} = $node->{name};
110 } elsif ( $node->{type} == XML_ATTRIBUTE_NODE ) {
113 $_xmllist[$node->{parent}]->setAttributeNS($node->{ns}, $node->{name}, $node->{value});
115 $_xmllist[$node->{parent}]->setAttribute($node->{name}, $node->{value});
123 $_xmllist[$node->{id}] = $xml;
125 if (defined $node->{parent}) {
126 $_xmllist[$node->{parent}]->addChild($xml);
130 $doc->setDocumentElement($_xmllist[0]);
135 # --------------------------------------------------------------
136 # -- Builds a list of nodes from a given xml doc
137 sub _xml_to_nodeset {
139 my($self, $doc) = @_;
142 return undef unless($doc);
144 my $node = $doc->documentElement;
145 return undef unless($node);
148 $self->{next_id} = 0;
150 push @{$self->{nodelist}}, {
153 name => $node->localname,
155 type => $node->nodeType,
156 ns => $node->namespaceURI
159 $self->_nodeset_recurse( $node, 0);
164 sub _nodeset_recurse {
166 my( $self, $node, $parent) = @_;
167 return undef unless($node && $node->nodeType == 1);
170 for my $kid ( ($node->getNamespaces, $node->attributes, $node->childNodes) ) {
172 my $type = $kid->nodeType;
174 push @{$self->{nodelist}}, {
175 id => ++$self->{next_id},
177 name => $kid->localname,
178 value => _grab_content( $kid, $type ),
180 ns => ($type != 18 ? $kid->namespaceURI : undef )
183 return if ($type == 3);
184 $self->_nodeset_recurse( $kid, $self->{next_id});
190 my $type = 1 << shift();
192 return undef if ($type & 1 << XML_ELEMENT_NODE);
193 return $node->textContent if ($type & $_tC_mask);
194 return $node->value if ($type & $_val_mask);
195 return $node->getData if ($type & 1 << XML_PI_NODE);