Justudo has asked for the wisdom of the Perl Monks concerning the following question:

Hi perl monks. I have an XML file as below, storing attributes of a list of files.
<file> <name>test.doc</name> <userprop> <nameprop>Effective Date</nameprop> <valueprop>16-Dec-2003</valueprop> </userprop> <userprop> <nameprop>Owner</nameprop> <valueprop>Adrian.Johnston</valueprop> </userprop> <userprop> <nameprop>Version</nameprop> <valueprop>v1.3</valueprop> </userprop> </file> <file> ... </file>
So if I would require all the files with version "v1.3", I would have to extract all "nameprop" nodes as below, and if the string is "Version", i'll get the equivalent string of valueprop and then do a comparison.
my $nodeset = $xpath->find('//file/userprop/nameprop'); ... $valu_node = $xpath->find('../valueprop',$node)->get_node(1);
However, if the XML for userprops was stored as attributes as below :
<userprop name="Version">v1.3</userprop>
Then, I could have taken easier XPath search of :
my $nodeset = $xpath->find('//file/userprop/[@name='v1.3]');
to derive all the version nodes. And then if they are "v1.3", I print out the file name. So qn is, is there any simple way to use XPath if my XML format is using sub-elements for "userprop", as in the first listing above. Thanks.

Replies are listed 'Best First'.
Re: XPath search qn
by pizza_milkshake (Monk) on May 05, 2004 at 05:38 UTC
    well, i figured out a way to perform tests on more than one child node, but it ain't pretty. i'd stick with attributes.

    update: updated the path to suck less. i learned a decent amount about xpath by doing this! :)

    #!perl -l use strict; use warnings; use XML::XPath; my $xp = XML::XPath->new("xml" => do { undef $/; <DATA> }); my $path = q{//userprop[nameprop='Version' and valueprop='v1.3']/ances +tor::file}; my $nodes = $xp->find($path); for my $node ($nodes->get_nodelist) { print XML::XPath::XMLParser::as_string($node); } __DATA__ <files> <file> <name>test.doc</name> <userprop> <valueprop>v1.3</valueprop> <nameprop>Version</nameprop> </userprop> </file> <file> <name>test.doc</name> <userprop> <nameprop>Version</nameprop> <valueprop>v1.4</valueprop> </userprop> </file> </files>

    perl -e"\$_=qq/nwdd\x7F^n\x7Flm{{llql0}qs\x14/;s/./chr(ord$&^30)/ge;print"

      I'd stick to attributes too if I had a choice! :) But your code is useful. Thanks. However, what if I want to do something like a wildcard search on 'v1.3' or 'v1.31' etc. ? I tried :
      my $nodes = $xp->find(q{/files/file/userprop/nameprop[text()='Version' +]/../valueprop[text()='v1.3|v1.31']/ancestor::file});
      AND
      my $nodes = $xp->find(q{/files/file/userprop/nameprop[text()='Version' +]/../valueprop[text()='v1.3*']/ancestor::file});
      Both did not work. Is it also possible to do a range search ? Like substr(text(),1,2) > 1.3 to get all version greater than 'v1.3'. Or even a date search ?
Re: XPath search qn
by dakkar (Hermit) on May 05, 2004 at 12:08 UTC
    //file[userprop[nameprop='Version']/valueprop=2]
    Which means:
    • take every file element
    • whose userprop child (whose nameprop child has a content of 'Version') has a valueprop child with a content of 2
    Or, put more clearly, you can nest node-tests (the things inside the brackets)
    -- 
            dakkar - Mobilis in mobile
    

    Most of my code is tested...

    Perl is strongly typed, it just has very few types (Dan)

      ah, i'd been looking for the ability to perform more than one test at the same level and not finding it. i will update my code. thanks!

      perl -e"\$_=qq/nwdd\x7F^n\x7Flm{{llql0}qs\x14/;s/./chr(ord$&^30)/ge;print"