1
votes

I am new to XPath. I need help with the XPath needed to extract the Book's Title and Authors towards the end of this XML. I tried the following C# code with no success. I just need to list the Book's Title and Authors under . Looks like the xmlns namespace affects my XPaths. My code works if I manually remove the xmlns. So, either I modify the XPath to account for this namespace or figure out a way to remove that attribute from the XML. Please advise.

Here is the C# code:

XmlNodeList nodes = XML.DocumentElement.SelectNodes("//Title");
foreach (XmlNode node in nodes)
{
Console.WriteLn(node.Name + " = " + node.InnerText); }
}

Here is the XML:

<?xml version="1.0"?>
<ItemSearchResponse xmlns="http://webservices.amazon.com/AWSECommerceService/2011-08-01">
  <OperationRequest>
    <RequestId>xxxxx</RequestId>
    <Arguments>
      <Argument Name="Condition" Value="All"></Argument>
      <Argument Name="ResponseGroup" Value="Small,Images"></Argument>
      <Argument Name="SearchIndex" Value="Books"></Argument>
    </Arguments>
    <RequestProcessingTime>0.0735170000000000</RequestProcessingTime>
  </OperationRequest>
  <Items>
    <Request>
      <IsValid>True</IsValid>
      <ItemSearchRequest>
        <Condition>All</Condition>
        <Keywords>Perl</Keywords>
        <ResponseGroup>Small</ResponseGroup>
        <ResponseGroup>Images</ResponseGroup>
        <SearchIndex>Books</SearchIndex>
      </ItemSearchRequest>
    </Request>
    <TotalResults>3761</TotalResults>
    <TotalPages>377</TotalPages>
    <MoreSearchResultsUrl>http://www.amazon.com/gp/redirect.html?camp=2025&amp;creative=386001&amp;location=http%3A%2F%2Fwww.amazon.com%2Fgp%2Fsearch%3Fkeywords%3DPerl%26url%3Dsearch-alias%253Dstripbooks&amp;linkCode=xm2&amp;tag=geo01d-20&amp;SubscriptionId=AKIAJJBQEKP2X72RQ6XA</MoreSearchResultsUrl>
    <Item>
      <ASIN>1449303587</ASIN>
      <DetailPageURL>http://www.amazon.com/Learning-Perl-Randal-L-Schwartz/dp/1449303587%3FSubscriptionId%3DAKIAJJBQEKP2X72RQ6XA%26tag%3Dgeo01d-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D165953%26creativeASIN%3D1449303587</DetailPageURL>
      <ItemLinks>
        <ItemLink>
          <Description>Technical Details</Description>
          <URL>http://www.amazon.com/Learning-Perl-Randal-L-Schwartz/dp/tech-data/1449303587%3FSubscriptionId%3DAKIAJJBQEKP2X72RQ6XA%26tag%3Dgeo01d-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D1449303587</URL>
        </ItemLink>
        <ItemLink>
          <Description>All Offers</Description>
          <URL>http://www.amazon.com/gp/offer-listing/1449303587%3FSubscriptionId%3DAKIAJJBQEKP2X72RQ6XA%26tag%3Dgeo01d-20%26linkCode%3Dxm2%26camp%3D2025%26creative%3D386001%26creativeASIN%3D1449303587</URL>
        </ItemLink>
      </ItemLinks>
      <SmallImage>
        <URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL75_.jpg</URL>
        <Height Units="pixels">75</Height>
        <Width Units="pixels">58</Width>
      </SmallImage>
      <MediumImage>
        <URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL160_.jpg</URL>
        <Height Units="pixels">160</Height>
        <Width Units="pixels">123</Width>
      </MediumImage>
      <LargeImage>
        <URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL.jpg</URL>
        <Height Units="pixels">500</Height>
        <Width Units="pixels">385</Width>
      </LargeImage>
      <ImageSets>
        <ImageSet Category="primary">
          <SwatchImage>
            <URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL30_.jpg</URL>
            <Height Units="pixels">30</Height>
            <Width Units="pixels">23</Width>
          </SwatchImage>
          <SmallImage>
            <URL>http://ecx.images-amazon.com/images/I/51kTNE8aIXL._SL75_.jpg</URL>
            <Height Units="pixels">75</Height>
            <Width Units="pixels">58</Width>
          </SmallImage>
        </ImageSet>
      </ImageSets>
      <ItemAttributes>
        <Author>Randal L. Schwartz</Author>
        <Author>brian d foy</Author>
        <Author>Tom Phoenix</Author>
        <Manufacturer>O'Reilly Media</Manufacturer>
        <ProductGroup>Book</ProductGroup>
        <Title>Learning Perl</Title>
      </ItemAttributes>
    </Item>
  </Items>
</ItemSearchResponse>
3

3 Answers

1
votes

You can do this with Linq-to-XML:

XDocument xmlDoc = XDocument.Parse(xmlString);
var q = from el in xmlDoc.Descendants()
                  .Where(x => x.Name.LocalName == "Title" || x.Name.LocalName == "Author") 
                   select el;

foreach (var xElement in q)
{
    Debug.WriteLine(xElement.Name.LocalName + " : " + xElement.Value);
}

The Output:

Author : Randal L. Schwartz
Author : brian d foy
Author : Tom Phoenix
Title : Learning Perl
0
votes

The XPath needs to be "//p:Title", and you need to tell the XPath processor that the namespace prefix "p" stands for the namespace "http://webservices.amazon.com/AWSECommerceService/2011-08-01". The way you establish namespace bindings depends on the API of your chosen XPath processor. For C# you can find an explanation of how to do it here (or in many, many other previous StackOverflow answers):

Xml-SelectNodes with default-namespace via XmlNamespaceManager not working as expected

0
votes

I finally figured out an answer to my question. I simply removed the namespace which overly complicated my XPath's. The C# code below works. PrintKeyValue simply prints Key = Value.

            XML = new XmlDocument();

            using (XmlTextReader reader = new XmlTextReader("C:\\Path\\File.xml"))
            {
                reader.Namespaces = false;
                XML.Load(reader);
            }

            XmlNodeList items = XML.DocumentElement.SelectNodes("//Item");
            foreach (XmlNode item in items)
            {
                PrintKeyValue("ISBN10", item["ASIN"].InnerText);

                XmlNode attrib = item.SelectSingleNode(".//ItemAttributes");
                PrintKeyValue("Title", attrib["Title"].InnerText);

                XmlNodeList authors = attrib.SelectNodes(".//Author");
                StringBuilder sb = new StringBuilder();
                int count = 0;
                foreach (XmlNode author in authors)
                {
                    count++;
                    if (count > 1) { sb.Append(", "); }
                    sb.Append(author.InnerText);
                }
                PrintKeyValue("Authors", sb.ToString());
            }