Reputation: 231
I am using the CPAN XML::LibXML
module to process the XML data below. I need to to determine whether each element has a child element or not. Searching around I can't find any example for that purpose.
<A>
<ts>2012</ts>
<T>M1</T>
<T>M2</T>
<B>
<id>PC</id>
<r>10</r>
<r>30</r>
</B>
</A>
This is the Perl code I havae written
#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;
my ($x,$elname,$haschild)= ();
my $parser = XML::LibXML->new();
my $npo = $parser->parse_file("test.xml");
my $rootel = $npo -> getDocumentElement();
$elname = $rootel -> nodeName();
print "Root name=$elname\n";
foreach $x ($rootel->childNodes) {
$elname = $x -> nodeName();
$haschild = $x->hasChildNodes;
print "Child name = $elname and has child = $haschild.\n" unless ($elname =~ /#text/i);
}
While I used childNodes
to go through each node, I just can't find an easy way to determine whether the node has a child or not.
I am expecting to get result after looping through all the nodes that:
A: Has children
ts: Has none
T: has none
T: has none
B: Has children
id: Has none
r: Has none
r: Has none
The result I am getting is like this:
Root name=A
Child name = ts and has child = 1.
Child name = T and has child = 1.
Child name = T and has child = 1.
Child name = B and has child = 1.
It seems all nodes return true after the hasChildNodes
condition check.
Upvotes: 2
Views: 4636
Reputation: 2034
If you just need to know if a child node exists then the test:
$node->exists('*')
would be more efficient than:
$node->findnodes('*')->size
because it would exit as soon as the first node had been found.
Upvotes: 1
Reputation: 126742
What you are asking for is the number of child elements of a node. The child nodes would include text and insignificant whitespace.
The easiest way to count the number of child elements a node has is to use findnodes('*')->size
as the XPath expression *
counts only child elements.
Here is some code that does what you describe
use v5.14;
use warnings;
use XML::LibXML;
my $xml = XML::LibXML->load_xml(string => <<XML);
<A>
<ts>2012</ts>
<T>M1</T>
<T>M2</T>
<B>
<id>PC</id>
<r>10</r>
<r>30</r>
</B>
</A>
XML
my $nodes = $xml->findnodes('//*');
foreach my $node ($nodes->get_nodelist) {
my $children;
for ($node->findnodes('*')->size) {
$children = 'none' when 0;
$children = '1 child' when 1;
default { $children = "$_ children" }
}
printf "%s: has %s\n", $node->localname, $children;
}
output
A: has 4 children
ts: has none
T: has none
T: has none
B: has 3 children
id: has none
r: has none
r: has none
Upvotes: 5
Reputation: 241968
What about the hasChildNodes
method?
use XML::LibXML;
my $xml = XML::LibXML->createDocument;
$xml->setDocumentElement($xml->createElement('root'));
$xml->documentElement->addChild($xml->createElement('son'));
for my $node ($xml->documentElement,
$xml->documentElement->firstChild) {
print $node->hasChildNodes, "\n";
}
Prints
1
0
Keep in mind that a text node is a child node, too (i.e. node and element are different notions).
Upvotes: 3