tags:

views:

146

answers:

2

I have been scratching my head trying to figure out how to use C#/XPath/SelectNodes to extract nodes from a Yahoo API XML result.

I essentially have the same problem as stated under

C# XmlDocument SelectNodes as well as under SelectNodes not working on stackoverflow feed

The point I'm stuck on is understanding exactly what to use as the xmlns, and why/whether I need to use a prefix when referencing the node in XPath as the XML nodes themselves have no prefix. .Net 3.5 is not an option for this project.

My code attempt (one of several iterations):

        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xml);

        XmlNameTable table = new NameTable();
        XmlNamespaceManager mgr = new XmlNamespaceManager(table);
        mgr.AddNamespace("lcl", "urn:yahoo:lcl");

        XmlNodeList nodes = doc.GetElementsByTagName("//lcl:ResultSet/Result");
        // !nodes.Count is zero.

and the XML I'm working with:

<?xml version="1.0"?>
<ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns="urn:yahoo:lcl" 
xsi:schemaLocation="urn:yahoo:lcl http://local.yahooapis.com/LocalSearchService/V3/LocalSearchResponse.xsd" 
totalResultsAvailable="35" totalResultsReturned="1" firstResultPosition="1">
<ResultSetMapUrl>http://maps.yahoo.com/broadband/?q1=San+Diego%2C+CA&amp;amp;tt=Pizza&amp;amp;tp=1&lt;/ResultSetMapUrl&gt;
<Result id="20850086"><Title>Filippi's Pizza Grotto</Title><Address>1747 India St</Address>
<City>San Diego</City><State>CA</State><Phone>(619) 232-5094</Phone><Latitude>32.723421</Latitude>
<Longitude>-117.168194</Longitude><Rating><AverageRating>4</AverageRating>
<TotalRatings>115</TotalRatings><TotalReviews>32</TotalReviews><LastReviewDate>1246565979</LastReviewDate>
<LastReviewIntro>... edited ...</ClickUrl>
<MapUrl>http://maps.yahoo.com/maps_result?q1=1747+India+St+San+Diego+CA&amp;amp;gid1=20850086&lt;/MapUrl&gt;
<BusinessUrl>http://www.realcheesepizza.com/&lt;/BusinessUrl&gt;
<BusinessClickUrl>http://www.realcheesepizza.com/&lt;/BusinessClickUrl&gt;&lt;Categories&gt;&lt;Category id="96926243">Pizza</Category>
<Category id="96926190">Italian Restaurants</Category>
<Category id="96926233">Continental Restaurants</Category>
<Category id="96926234">Carry Out &amp; Take Out</Category><Category id="96926236">Restaurants</Category>
</Categories></Result></ResultSet>
<!-- ws01.ydn.gq1.yahoo.com uncompressed/chunked Thu Aug 20 17:56:17 PDT 2009 -->
+2  A: 

Try

XmlNodeList nodes = doc.SelectNodes("//lcl:ResultSet/lcl:Result",mgr);

The nodes in the xml do not have a prefix but they do have a namespace as specified by xmlns="...". Therefore when doing an XPath query you need to provide a namespace for the elements you are searching for. The namespace manager and the prefix allow you to do this.

Your query would probably work a little quicker using just a single slash.

XmlNodeList nodes = doc.SelectNodes("/lcl:ResultSet/lcl:Result",mgr);
Darrel Miller
That did not do the trick :-( I'm confused about the actual namespace. Given the line `mgr.AddNamespace("lcl", "urn:yahoo:lcl");`, is "urn:yahoo:lcl" correct? Other examples I have seen have an "http://...." format.
Eric J.
I'm not 100% sure but I think namespaces can look like pretty much anything. There is no predefined format. By convention people tend to use an URL styled namespace, but that is only a convention. I'm missed the NameTable error that Tomalak picked up.
Darrel Miller
Thanks for the info about namespace and for trying to help solve this.
Eric J.
+2  A: 

I can't help but say that this looks a bit like homework. There are some mistakes in the code that leave the impression of being deliberately made wrong.

  • Why do you feed an XPath expression to GetElementsByTagName()?
  • Why do you create a new NameTable that has no relation to the actual XML document?
  • Why do you use a namespace prefix for the first element in the path (ResultSet), but not for the second (Result), even though they both are in the same namespace?

This works:

XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);

XmlNamespaceManager mgr = new XmlNamespaceManager(doc.NameTable);
mgr.AddNamespace("lcl", "urn:yahoo:lcl");

XmlNodeList nodes = doc.SelectNodes("/lcl:ResultSet/lcl:Result", mgr);
Tomalak
No not homework, just using XPath with namespaces for the first time :-) The key was using the existing name table from the document.
Eric J.