in reply to Xpath not working

speaking of xpather.pl here is what you might write
my $xpath = qq{ /*[ name() = "p:login" ] /*[ name() = "utenti" ] [ *[ name() = "email" and string(node()) = "$email" ] and *[ name() = "password" and string(node()) = "$password" ] ] };

no need to register any namespaces and things like that (some info on that Re: LibXML, XPath and Namespaces (name(), local-name()))

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

    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)

      ... 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.