Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation

Perl XPath

by GoForIt (Novice)
on Feb 27, 2012 at 19:10 UTC ( #956496=perlquestion: print w/replies, xml ) Need Help??
GoForIt has asked for the wisdom of the Perl Monks concerning the following question:

Hello there, I'm trying to find and replace a string value in XML. I've the below code so far.

#!C:/Perl/bin/perl.exe -w #use strict; use XML::XPath; use Data::Dumper; use XML::XPath::XMLParser; my $xp = XML::XPath->new(filename => "new.xml"); my $nodeset = $xp->findnodes('/catalog/book/titles/title'); my @n = ("value one", "value two"); print $nodeset->size; my $i = 0; foreach my $node ($nodeset->get_nodelist) { print "\nGET: " . $xp->getNodeText($node) . "\n"; print "\nSET: " . $xp->setNodeText('/catalog/book/titles/title['." +$i+1".']', $n[$i]) . "\n"; $i++; } open('FILE', '>output.xml'); my $nodes = $xp->find('/'); foreach my $node ($nodes->get_nodelist) { print FILE XML::XPath::XMLParser::as_string($node); } close(FILE);

My sample XML file

<?xml version="1.0"?> <catalog> <book> <author>Gambardella, Matthew</author> <titles> <title>Book 1 </title> <title>Book 2 </title> </titles> <genre>Computer</genre> <price>44.95</price> <publish_date>2000-10-01</publish_date> <description>An in-depth look at creating applications with XML.</description> </book> <book> <author>Ralls, Kim</author> <titles> <title>Book 3 </title> <title>Book 4 </title> </titles> <genre>Fantasy</genre> <price>5.95</price> <publish_date>2000-12-16</publish_date> <description>A former architect battles corporate zombies, an evil sorceress, and her own childhood to become queen of the world.</description> </book> </catalog>

I need to pass each node value to the setNodeText method. The XPath I've currently sets two child nodes at a time.

For example, I'd like to retrieve Book 1 through 4 one at a time and set each node value to a different value. In my code, when I try to set Book 1, it sets Book 3 to the same value. Likewise, when I try to set Book 2, the value of Book 4 is overwritten. Is there a way that I could pass one node to the setNodeText() method?

I appreciate any input.


Replies are listed 'Best First'.
Re: Perl XPath
by Corion (Pope) on Feb 27, 2012 at 19:27 UTC

    Your code affects two title nodes at once because your XPath expression matches two title nodes:

    /catalog/book/titles/title[ $i+1 ]

    You will need to either make your XPath expression match the specific book you are currently modifying:

    /catalog/book[ $j ]/titles/title[ $i+1 ]

    ... or you have to remove the text node from the current element and append your own text node. See XML::LibXML::Element->appendTextChild and XML::LibXML::Node->removeChildNodes:

    my $element = ...; $element->removeChildNodes(); $element->appendTextNode("Hello!");


      Thanks for your response.

      Is there a way that I could pass a node to setNodeText()? I'm able to retrieve the different node values using getNodeText() method by passing the $node parameter. I'm assuming that I could do the same for setNodeText() method but somehow the method doesn't recognize the $node in XPath.


        I assume once you have the XML::LibXML::Text node, you can use ->replaceData or ->setData to change the values. But I've never done anything like it using XML::LibXML, so you'll have to find out whether it works yourself.

Re: Perl XPath
by BrowserUk (Pope) on Feb 28, 2012 at 20:53 UTC

    Try this. It takes 3 seconds to update a generated file containing 1000 books (2000 titles):

    #! perl -slw use strict; use Data::Dump qw[ pp ]; use XML::Simple; my $start = time; my $xml = XMLin( \*STDIN, KeepRoot => 1 ); #pp $xml; for my $book ( 0 .. $#{ $xml->{catalog}{book} } ) { my $titles = $xml->{catalog}{book}[ $book ]{ titles }{ title }; for my $title ( 0 .. $#{ $titles } ) { my $txt = $titles->[ $title ]; $txt =~ s[(\d+)]{ $1+1 }e; $titles->[ $title ] = $txt; } } print XMLout( $xml, KeepRoot => 1, NoAttr => 1 ); warn time() - $start; __END__ C:\test>junk < junk.dat >junk2.dat 3 at C:\test\ line 22.

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://956496]
Approved by Corion
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others contemplating the Monastery: (3)
As of 2018-05-27 20:26 GMT
Find Nodes?
    Voting Booth?