Ошибка в выражении xpath undefined namespace prefix

Very new to PHP/programming in general, and I’ve been trying to run a PHP file set up to parse an Atom feed outputted from the Google Search API for Shopping. When run however, the code outputs a large number enter code heref errors (warnings), all having to do with XPath:

Warning: SimpleXMLElement::xpath() [simplexmlelement.xpath]: Undefined namespace prefix in /home/ultradea/public_html/gtest/search.php on line 205

Warning: SimpleXMLElement::xpath() [simplexmlelement.xpath]: xmlXPathEval: evaluation failed in /home/ultradea/public_html/gtest/search.php on line 205

Warning: Invalid argument supplied for foreach() in /home6/legionit/public_html/pricemash/search.php on line 289

The full code I’m working with can be found here: search.php

Here’s the uploaded copy I’m working with that outputs the errors — http://ultra-deals.com/gtest/search.php

And here’s the specific code in question

Lines 204-207
$xml = simplexml_load_string($return);
$result = $xml->xpath('//openSearch:totalResults');
$START = array_shift($xml->xpath('openSearch:startIndex'));
$RESULTS = array_shift($xml->xpath('openSearch:totalResults'));
<...> Line 261
$spelling_suggestion = array_shift($xml->xpath('s:spelling/s:suggestion'));
<...> Lines 273-280
$promos = "";
foreach ($xml->xpath('s:promotions/*') as $item) {
   $promos .= "<td><a href='$item[link]'>$item[description]";
   if ($item[imageLink] != "") {
     $promos .= "<br/><img src='$item[imageLink]'>";
   }
   $promos .= "</td>";
}
<...> Lines 328-335
$NARROW = "";
foreach ($xml->xpath('s:facets/*') as $item) {
 $values = $item->xpath('s:bucket');
 $type = $item['type'];
 $attrib_name = $item['name'];
 if ($attrib_name == "") {
   $attrib_name = $item['property'];  // Default types
 }

Each of those correspond to one or more of the error messages outputted on the search.php file I uploaded to my server. I have no experience with XPath, so I’m not even exactly sure what the namespace prefix in question is. Really appreciate the help!

The channel is not a child node of the document element. If you read the XML the document element is query with a child results. I suggest storing the channel in its own variable:

$channel = $xml->results->channel;

You will have to register the namespace on each SimpleXMLElement before you use its xpath() method. In your case you register it on the $xml, But you do not register it on the $channel or $item objects.
$xml = simplexml_load_string($result);

$channel = $xml->results->channel;
$channel->registerXPathNamespace('yweather', 'http://xml.weather.yahoo.com/ns/rss/1.0');

$weather_location = $channel->xpath('yweather:location');
if(!empty($weather_location)){
  foreach($channel->item as $item){
    $item->registerXPathNamespace('yweather', 'http://xml.weather.yahoo.com/ns/rss/1.0');

    $forecast = $item->xpath('yweather:forecast');    
    var_dump($forecast);     
  }      
}

Output:

array(10) {
  [0]=>
  object(SimpleXMLElement)#8 (1) {
    ["@attributes"]=>
    array(6) {
      ["code"]=>
      string(2) "30"
      ["date"]=>
      string(11) "31 Mar 2016"
      ["day"]=>
      string(3) "Thu"
      ["high"]=>
      string(2) "70"
      ["low"]=>
      string(2) "41"
      ["text"]=>
      string(13) "Partly Cloudy"
    }
  }
  [1]=>
  object(SimpleXMLElement)#9 (1) {
   ...

DOM uses a separate object for the XPath expressions, so you will only have to register the namespace on this object:

$document = new DOMDocument();
$document->loadXml($result);
$xpath = new DOMXpath($document);
$xpath->registerNamespace('yweather', 'http://xml.weather.yahoo.com/ns/rss/1.0');

$weather_location = $xpath->evaluate('//yweather:location');
...

So I have this XML response, and when I try to generate an XPath and pull data from it, it won’t work. 

I’ve loaded this into Notepad++ and used the XML plugin to select a node/element/whatever the correct verbiage, and it tells me the XPath, but then when I try to execute that XPath to get the value back, it fails. I see similar behavior in XMLstarlet and sites like xmlgrid.net.

E.g. if I highlight row 7 (the ABANumber), the XML plugin tells me the xpath could be «/s:Envelope/s:Body/GetDealerResponse/GetDealerResult/a:Dealer/a:ABANumber». If I then go to «Evaluate XPath expression» and paste in that exact same XPath, it says «Error: unable to evaluate xpath expression» and the xpath that I pasted.

Bonus question: How much do you hate XML? Why does this shit have to be so convoluted and complicated?

XML

<?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope">
  <s:Body>
    <GetDealerResponse xmlns="http://tempuri.org">
      <GetDealerResult xmlns:a="http://schemas.datacontract.org/2004/07/Definitive.Admin.Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <a:Dealer>
          <a:ABANumber>1234</a:ABANumber>
          <a:ACAStatusId i:nil="true"/>
          <a:AFNStatusId i:nil="true"/>
          <a:AFStatusId i:nil="true"/>
          <a:AccountNumber>5678</a:AccountNumber>
          <a:ActivatedDate>2012-01-17T00:00:00</a:ActivatedDate>
          <a:Address>28555 somewhere</a:Address>
          <a:AssignedRole1UserId i:nil="true"/>
          <a:AssignedRole1UserName/>
          <a:AssignedRole2UserId i:nil="true"/>
          <a:AssignedRole2UserName/>
          <a:AssignedRole3UserId i:nil="true"/>
          <a:AssignedRole3UserName/>
          <a:AssignedRole4UserId i:nil="true"/>
          <a:AssignedRole4UserName/>
          <a:AssignedRole5UserId i:nil="true"/>
          <a:AssignedRole5UserName/>
          <a:BankAccountNum i:nil="true"/>
          <a:BankAccountTyp i:nil="true"/>
          <a:BankCity i:nil="true"/>
          <a:BankName i:nil="true"/>
          <a:BankPhoneNum i:nil="true"/>
          <a:BankRoutingNum i:nil="true"/>
          <a:BankSetupDate i:nil="true"/>
          <a:BankState i:nil="true"/>
          <a:Bookout>1</a:Bookout>
          <a:Branch/>
          <a:City>Wesley Chapel</a:City>
          <a:ClientDealerId>321104</a:ClientDealerId>
          <a:ClientId>23</a:ClientId>
          <a:Contacts>
            <a:ContactData>
              <a:ContactId>32654</a:ContactId>
              <a:Email>someone@somewhere.com</a:Email>
              <a:Fax></a:Fax>
              <a:Name></a:Name>
              <a:Phone></a:Phone>
              <a:Title>Finance Mgr 1</a:Title>
            </a:ContactData>
            <a:ContactData>
              <a:ContactId>50615</a:ContactId>
              <a:Email>@.com</a:Email>
              <a:Fax i:nil="true"/>
              <a:Name></a:Name>
              <a:Phone></a:Phone>
              <a:Title>Finance Mgr 2</a:Title>
            </a:ContactData>
          </a:Contacts>
          <a:CustomFields>
            <a:CustomFieldData>
              <a:BoolValue i:nil="true"/>
              <a:DateTimeValue i:nil="true"/>
              <a:DecimalValue i:nil="true"/>
              <a:FieldId>0</a:FieldId>
              <a:FieldName>NTL Pods</a:FieldName>
              <a:NumericValue i:nil="true"/>
              <a:StringValue/>
            </a:CustomFieldData>
            <a:CustomFieldData>
              <a:BoolValue i:nil="true"/>
              <a:DateTimeValue i:nil="true"/>
              <a:DecimalValue i:nil="true"/>
              <a:FieldId>0</a:FieldId>
              <a:FieldName>New Dealer Agreement Needed</a:FieldName>
              <a:NumericValue i:nil="true"/>
              <a:StringValue>Yes</a:StringValue>
            </a:CustomFieldData>
            <a:CustomFieldData>
              <a:BoolValue i:nil="true"/>
              <a:DateTimeValue i:nil="true"/>
              <a:DecimalValue i:nil="true"/>
              <a:FieldId>0</a:FieldId>
              <a:FieldName>New ACH Needed</a:FieldName>
              <a:NumericValue i:nil="true"/>
              <a:StringValue/>
            </a:CustomFieldData>
          </a:CustomFields>
          <a:DeActivatedDate>2015-06-09T00:00:00</a:DeActivatedDate>
          <a:DealerGroupId>0</a:DealerGroupId>
          <a:DealerGroupName/>
          <a:DealerId>12032</a:DealerId>
          <a:DealerName> OF WESLEY CHAPEL</a:DealerName>
          <a:DealerTrackDealerId/>
          <a:Dealers i:nil="true"/>
          <a:DocumentDelivery>Both</a:DocumentDelivery>
          <a:Email>none@n.com</a:Email>
          <a:EnrollmentDate>2012-01-17T00:00:00</a:EnrollmentDate>
          <a:ExternalId i:nil="true"/>
          <a:FaxNum></a:FaxNum>
          <a:GlobalDealerId>f5b77f60-5779-e311-a7b3-0050569f4a64</a:GlobalDealerId>
          <a:Identifier i:nil="true"/>
          <a:IsActive>false</a:IsActive>
          <a:IsActiveForDealerTrack>false</a:IsActiveForDealerTrack>
          <a:IsActiveForRouteOne>false</a:IsActiveForRouteOne>
          <a:LotTypCd i:nil="true"/>
          <a:Market i:nil="true"/>
          <a:MultiDecisionEnabled>false</a:MultiDecisionEnabled>
          <a:NADARegion i:nil="true"/>
          <a:Notes>Transferred  4/17/15. Dealer has been sold no longer in business.</a:Notes>
          <a:NotificationsMode>Both</a:NotificationsMode>
          <a:PhoneNum></a:PhoneNum>
          <a:PostalCd>33543</a:PostalCd>
          <a:PrimaryAddress i:nil="true"/>
          <a:Rating>3</a:Rating>
          <a:RepresentativeUserId>346</a:RepresentativeUserId>
          <a:RepresentativeUserName>SWF</a:RepresentativeUserName>
          <a:RouteOneDealerId/>
          <a:ServiceSystemId1/>
          <a:ServiceSystemId2/>
          <a:SourceDealerId i:nil="true"/>
          <a:StateCd>FL        </a:StateCd>
          <a:StateCdId>12</a:StateCdId>
          <a:TerminationDate i:nil="true"/>
          <a:Type>Franchise</a:Type>
          <a:Warnings i:nil="true"/>
        </a:Dealer>
        <a:Errors/>
        <a:ResponseEndTime>2016-08-25T15:00:43.3302077-05:00</a:ResponseEndTime>
        <a:ResponseStartTime>2016-08-25T15:00:43.3302077-05:00</a:ResponseStartTime>
      </GetDealerResult>
    </GetDealerResponse>
  </s:Body>
</s:Envelope>

I’m not sure why, but it seems that you have to drop the namespace prefix to get the node:

xmlfeed.at_xpath("//totalresults")

Also note that I added the double forward slash, which scopes the search over the whole document (it won’t work without it).

UPDATE:

Based on this answer: How do I get Nokogiri to understand my namespaces? I’d guess that the namespace (openSearch:totalResults) is not correctly declared as an attribute on the root node of the document, and hence Nokogiri is just ignoring it, which is why the selector above works but the namespaced one doesn’t.

Comments

  • I am trying to parse Youtube Gdata to see if video with given id exists. But there isn’t normal tag but with namespace. On the link http://gdata.youtube.com/feeds/api/videos?q=KgfdlZuVz7I there is tag:

    <openSearch:totalResults>1</openSearch:totalResults>
    

    There is namespace openSearch:

    xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'
    

    but I dont know how to deal with it in Nokogiri and Ruby.

    Here is part of code:

    xmlfeed = Nokogiri::HTML(open("http://gdata.youtube.com/feeds/api/videos?q=#{video_id}"))
    xmlfeed.at_xpath("openSearch:totalResults")
    

    It gives error:

    Undefined namespace prefix: openSearch:totalResults
    

Recents

Omar


  • #1

Hi,
I would use your help parsing an XML file. I am new to XPath, and I
would like to use it parsing an XML file generated with dia, the file looks
like :
<?xml version=»1.0″ encoding=»UTF-8″?>
<dia:diagram xmlns:dia=»http://www.lysator.liu.se/~alla/dia/»>
<dia:diagramdata>
<dia:attribute name=»background»>
<dia:color val=»#ffffff»/>
</dia:attribute>
<dia:attribute name=»pagebreak»>
<dia:color val=»#000099″/>
</dia:attribute>
</dia:diagramdata>
</dia:diagram>

I would need to match all the «attribute» nodes. I don’t know wether the
XPath expression i’m using is wrong or whether the API (libxml2) i’m using
is loosy …
Trying «//attribute» matches nothing, «//dia:attribute» gives the following
libxml error : «XPath error : Undefined namespace prefix» and the wierdest
thing is that :
«//*» matches all nodes with no errors… «/*» matches the «diagram» node
with no errors but … «/diagram» matches nothing and neither does
«/dia:diagram» which actually gives the «XPath error : Undefined namespace
prefix». Can anyone help me !

Thanks in advance.

Advertisements

David Carlisle


  • #2

Omar said:

Hi,
I would use your help parsing an XML file. I am new to XPath, and I
would like to use it parsing an XML file generated with dia, the file looks
like :
<?xml version=»1.0″ encoding=»UTF-8″?>
<dia:diagram xmlns:dia=»http://www.lysator.liu.se/~alla/dia/»>
<dia:diagramdata>
<dia:attribute name=»background»>
<dia:color val=»#ffffff»/>
</dia:attribute>
<dia:attribute name=»pagebreak»>
<dia:color val=»#000099″/>
</dia:attribute>
</dia:diagramdata>
</dia:diagram>

I would need to match all the «attribute» nodes. I don’t know wether the
XPath expression i’m using is wrong or whether the API (libxml2) i’m using
is loosy …
Trying «//attribute» matches nothing,

It selects elements called attribute in no namespace, there are no such elements

«//dia:attribute» gives the following

libxml error : «XPath error : Undefined namespace prefix» and the wierdest
thing is that :

This is what you need, but you need to bind the prefix (which doesn;t
need to be teh same prefix as used in the source) to the namespace
http://www.lysator.liu.se/~alla/dia/
If the xpath is inside XSLT you bind a prefix using
xmlns:dia=»http://www.lysator.liu.se/~alla/dia/»
but if calling an XPath API directly it will have alternative calls to
set up these bindings before evaluating the Xpath.

«//*» matches all nodes with no errors…

That selects all elements in any namespace

«/*» matches the «diagram» node

That selects the top level element bode.

with no errors but … «/diagram» matches nothing

That selects the top level element if it is called diagram in no
namespace

and neither does
«/dia:diagram» which actually gives the «XPath error : Undefined namespace
prefix». Can anyone help me !

As above, this would work if you bind the dia prefix first.

If it is inconvenient to bind namespace prefixes in your API then you
have the alternative of
//*[local-name()=’attribute’]
for example. Which selects all elements with local name ‘attribute’ in
any namespace.

David

Patrick TJ McPhee


  • #3

[…]
% Trying «//attribute» matches nothing, «//dia:attribute» gives the following
% libxml error : «XPath error : Undefined namespace prefix» and the wierdest

If you’re using libxml directly, use xmlXPathRegisterNs() to register
a namespace, something like

xmlXPathRegisterNs(xpc, «dia», «http://www.lysator.liu.se/~alla/dia/»);

(where xpc is the xpath context pointer). The prefix (dia in this case)
doesn’t have to be the same as the prefix used in your xml file, but
the URL has to match the name space definition in the xml file exactly.

Omar


  • #4

Hi,
xmlXPathRegisterNs works quite fine. //*[local-name()=’attribute’] :
works fine too, but I’m not quite convinced that this last one is convinient
from performance standpoint. I’m anyway a little surprised to see that the
parser does not automatically perform this binding. But I suppose this is an
API specific question, and out of scope of this newsgroup.

Thanks for your help.

Omar

Advertisements

Patrick TJ McPhee


  • #5

% from performance standpoint. I’m anyway a little surprised to see that the
% parser does not automatically perform this binding. But I suppose this is an
% API specific question, and out of scope of this newsgroup.

You shouldn’t be surprised. The name-space prefix is just a place-holder
for the name-space uri. The parser has no way of knowing what URI your
name-space prefix refers to unless you perform the binding. In a lot of
cases (e.g., any document with a default name space), the XPath expression
has to use a different name-space prefix than the one used in the
source document.

Понравилась статья? Поделить с друзьями:
  • Ошибка в выражении xpath invalid expression
  • Ошибка в выпускном распредвале опель
  • Ошибка в вызванном внутреннем сервисе деловая среда
  • Ошибка в выгрузке в суфд
  • Ошибка в выбранной цели при принятии решения