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

Hi everyone, i'm trying to make a login function using perl and XML. I have a perl script that recieve an username ad a password and uses xpath to look for a node in a XML file containing the data. For some reasons the xpath fails and doesn't return any nodes. I'm sure the password and the username recieved by the script are correct. Here's the script and the XML file. Can someone please help me? I'm pretty noob and i cant figure this out.

my $file='../data/login.xml'; my $parser=XML::LibXML->new(); my $doc=$parser->parse_file($file) or die"err parser"; $doc->documentElement->setNamespace("http://www.crtp.it","p"); my $finded = $doc->findnodes("p:login/p:utenti[p:email ='$email' and +p:password='$digestpass']"); if($finded){ my $amministratore=$doc->findnodes("p:login/p:utenti[p:username =' +$email' and p:password='$digestpass']/p:admin"); my $sessione = CGI::Session->new(); my $user = $doc->findnodes("p:login/p:utenti[p:username ='$email' +and p:password='$digestpass']/p:nome"); $sessione->param("email","$email"); $sessione->param("amministratore","$amministratore"); $sessione->param("user","$user"); print $sessione->header(-location=>"fork.cgi"); }

finded remains empty. here's the XML

<?xml version="1.0" encoding="UTF-8"?> <p:login xmlns="http://www.crtp.it" xmlns:xsi="http://www.w3.org/2001/ +XMLSchema-instance" xmlns:p="http://www.crpt.com" xsi:schemaLocation= +"http://www.crpt.com login.xsd"> <utenti> <admin>n</admin> <email>geeno@gmail.com</email> <password>85136c79cbf9fe36bb9d05d0639c70c265c18d37 +</password> <nome>geeno</nome> <cognome>peeno</cognome> <sesso>Maschio</sesso> <dataNascita>26/10/1991</dataNascita> </utenti> <utenti> <admin>y</admin> <email>davide@gmail.com</email> <password>85136c79cbf9fe36bb9d05d0639c70c265c18d37 +</password> <nome>davide</nome> <cognome>santangelo</cognome> <sesso>Maschio</sesso> <dataNascita>26/10/1991</dataNascita> </utenti> </p:login>

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

    Yep, what the other Anon said. Here's the relevant code on how to actually do it:

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

      To correct my first sentence of the parent and expand the explanation a little:

      Your line $doc->documentElement->setNamespace("http://www.crtp.it","p"); does not work, and the documentation of XML::LibXML::Element->setNamespace explains why:

      The function fails if it is required to create a declaration associating the prefix with the namespace URI but the element already carries a declaration with the same prefix but different namespace URI.

      Which means you can't re-use the "p" prefix as it's already in use on the element; you'd have to pick a different prefix, then the setNamespace call would work and you wouldn't need to do registerNs on two namespaces.

      However, you would still need to make one XML::LibXML::XPathContext->registerNs set-up as shown in the parent.

      So TIMTOWTDI; personally I wouldn't manipulate the XML, but adjust my XPath to fit the XML (this is the solution shown in the parent, and you could remove the setNamespace call from your code).

Re: Xpath not working
by Anonymous Monk on Mar 27, 2014 at 20:11 UTC
    setting a namespace doesn't register a namespace ... but you don't need to register namespaces, just adjust your xpaths to use name(), see xpather.pl
Re: Xpath not working
by njack (Novice) on Mar 28, 2014 at 23:20 UTC

    Thank you all guys, i'm going to try your solution in the next days and i'll get you back with the result. In the meantine, thank you all for your help

Re: Xpath not working
by Anonymous Monk on Mar 28, 2014 at 18:11 UTC
    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()))

      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