in reply to Re: Xpath not working
in thread Xpath not working

While that XPath query certainly works with the example XML the OP gave, it should be noted it will stop working or give possibly unintended results in any of the following three cases, while the solution with registerNs will continue working correctly (being namespace-aware).

  1. The namespace prefix of the login element changes (and setNamespace isn't used)
  2. A namespace prefix is added on any of the nodes named in the XPath expression that currently don't have one
  3. Any elements are added to the document which happen to have the same prefix+name, regardless of their actual namespace (the expression will match those too)

Replies are listed 'Best First'.
Re^3: Xpath not working
by Anonymous Monk on Mar 28, 2014 at 22:30 UTC

    ... it should be noted it ...

    Thanks ; yeah, if the xml changes it change

    You can add attribute test

    and @xmlns="http://www.crtp.it"
    or use the xpath function
    and namespace-uri() = "http://www.crpt.com"

    The idea is to keep the xpath stuff where it belong, in the xpath string

      if the xml changes it change

      The three listed changes produce semantically equivalent XML documents. For example, if you make any or all of those changes, the document will still validate perfectly fine against the same schema. If the OP doesn't have full control over the source of the XML, those changes might happen, and the XPath using just name() would break.

      Even though TIMTOWTDI, I prefer writing this

      my $xpc = XML::LibXML::XPathContext->new($doc); $xpc->registerNs('p', 'http://www.crtp.it'); $xpc->registerNs('p2', 'http://www.crpt.com'); my $result = $xpc->findnodes("/p2:login/p:utenti[p:email='$email' and +p:password='$digestpass']/p:admin");

      over this

      my $result = $doc->findnodes(qq{ /*[ local-name()="login" and namespace-uri()="http://www.crpt.com" ] /*[ local-name()="utenti" and namespace-uri()="http://www.crtp.it" ] [ *[ local-name()="email" and namespace-uri()="http://www.crtp.it" a +nd string(node())="$email" ] and *[ local-name()="password" and namespace-uri()="http://www.crtp.it +" and string(node())="$digestpass" ] ] /*[ local-name()="admin" and namespace-uri()="http://www.crtp.it" ] });

      because it's shorter, has less repetition, and uses the recommended API.

      The idea is to keep the xpath stuff where it belong

      In the first piece of code above, the XPath stuff is already where it belongs: in an XPath expression context. From the XPath spec:

      Expression evaluation occurs with respect to a context. ... The context consists of:
      • ...
      • the set of namespace declarations in scope for the expression

      Also, it's what is recommended by findnodes:

      There are several possible ways to deal with namespaces in XPath:
      • The recommended way is to use the XML::LibXML::XPathContext module to define an explicit context for XPath evaluation, in which a document independent prefix-to-namespace mapping can be defined.

        Even though TIMTOWTDI, I prefer writing this

        Hah, my way :)

        over this

        Well, you only really need the first one ... unless all of a sudden the xml switches the default namespace ... which makes for weak xml

        because it's shorter, has less repetition, and uses the recommended API... the XPath stuff is already where it belongs: in an XPath expression context.

        Yes, the completely unspecified part of the api that is up to each implementer to implement

        To use the recommended API xpather.pl would have to switch to printing perl code instead of xpaths ....

        With xpather.pl you copy/paste the xpaths, edit minimally , practically no writing involved :) and even the dumbest newbie can copy/paste (and xml/xpath not exactly newbie friendly)

        FWIW , this is F2.0

        $node->F("//f:p/g:h", qw[ f http://f.example.com g http://g.example.co +m ]); sub XML::LibXML::Node::F { my $self = shift; my $xpath = shift; my %prefix = @_; our $XPATHCONTEXT; $XPATHCONTEXT ||= XML::LibXML::XPathContext->new(); while( my( $p, $u ) = each %prefix ){ $XPATHCONTEXT->registerNs( $p, $u ); } $XPATHCONTEXT->findnodes( $xpath, $self ); }