6

I'm calling a WebService exposed by Oracle that accepts an input of an ItemID and returns to me the corresponding Item Number. I want to grab the Item Number that has been returned out of the XML contained in the response.

The XML looks like this:

<env:Envelope
  xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:ns0="http://dev1/MyWebService1.wsdl">
 <env:Header>
  <wsse:Security
    xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    env:mustUnderstand="1"/>
 </env:Header>
 <env:Body>
  <ns0:getItemNbrByItemIdResponseElement>
   <ns0:result>1010603</ns0:result>
  </ns0:getItemNbrByItemIdResponseElement>
 </env:Body>
</env:Envelope>

I'm interested in grabbing only the <ns0:result>1010603</ns0:result> particularly only the 1010603.

I haven't done a lot of work parsing XML using C# and I'm playing around with a few different methods so far. What is the recommended way to do this?

I'm on VS2008 (so XPath is available etc.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Mat Nadrofsky
  • 8,289
  • 8
  • 49
  • 73

6 Answers6

15

I'd personally use LINQ to XML, because I find that easier to deal with than XPath, particularly when namespaces are involved. You'd do something like:

XNamespace ns0 = "http://dev1/MyWebService1.wsdl";

String result = doc.Descendants(ns0 + "result").First().Value;

Note that doc here is expected to be an XDocument, not an XmlDocument. (My guess is that this is why it wasn't showing up for you.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Agreed; without LINQ you'd need to use namespace-managers and all sorts - this is much simpler. – Marc Gravell Dec 17 '08 at 14:50
  • +1, namespaces "need" to be easier to work with which XLINQ does nicely – user7116 Dec 17 '08 at 15:12
  • When trying to use this VS is complaining about the .Descendants method. Despite having using System.Linq and System.Xml.Linq present. Any thoughts/what I'm missing? I certainly like how concise this solution is. – Mat Nadrofsky Dec 17 '08 at 15:15
  • @Jon - Yep that fixed up the issue there. Now just figuring out why it's stating that the sequence contains no elements. :) Thanks again for the fix! – Mat Nadrofsky Dec 17 '08 at 16:02
  • I needed to use an XMLNodeReader in order to properly populate the XDocument, that's all. Now it works like a champ. Thanks again Jon. – Mat Nadrofsky Dec 17 '08 at 16:29
5

fwiw you can cheat the namespace issue with an xpath like this: //*[local-name()='result']

annakata
  • 74,572
  • 17
  • 113
  • 180
3

If you don't want to go for Linq you could use XPathDocument to retrieve the value:

XPathDocument xmldoc = new XPathDocument(@"C:\tmp\sample.xml");
XPathNavigator nav = xmldoc.CreateNavigator();

XmlNamespaceManager nsMgr = new XmlNamespaceManager(nav.NameTable);
nsMgr.AddNamespace("ns0", "http://dev1/MyWebService1.wsdl");

XPathNavigator result = nav.SelectSingleNode("//ns0:result", nsMgr);
System.Diagnostics.Debug.WriteLine(result.Value);

XPathDocument has a lower memory footprint and is most likely faster in your scenario than XmlDocument. XmlDocument builds up a complete object model of your XML document in memory whereas XPathDocument does not do that.

Dirk Vollmar
  • 172,527
  • 53
  • 255
  • 316
2

Off the top of my head, the following should work:

XmlDocument doc = new XmlDocument();
doc.PreserveWhitespace = true;

XmlNamespaceManager mgr = GetNamespace(doc);
doc.LoadXml(xmltext);

XmlNode nd = doc.DocumentElement.SelectSingleNode("//ns0:result", mgr);

The namespace code looks like this:

private XmlNamespaceManager GetNamespace(XmlDocument document)
{
    XmlNamespaceManager mgr = new XmlNamespaceManager(document.NameTable);
    mgr.AddNamespace("ns0", "http://dev1/MyWebService1.wsdl");
    return mgr;
}

You need to use the namespace manager because the XML document has namespaces associated with it, and XPath uses this in query resolution.

Pete OHanlon
  • 9,086
  • 2
  • 29
  • 28
  • That's close... Just tried it out and it's giving me an error: Namespace Manager or XsltContext needed. This query has a prefix, variable, or user-defined function. – Mat Nadrofsky Dec 17 '08 at 15:02
  • My bad - the SelectSingleNode should have a reference to the namespace manager with it. I'll update it. – Pete OHanlon Dec 17 '08 at 15:05
  • Thanks for the fix! :) I could just step-up my game and learn a few things and then you wouldn't have to solve my problems for me! ;) – Mat Nadrofsky Dec 17 '08 at 15:17
1

To solve this, I used Jon Skeet's answer. Here's the code that I had to implement to make this work (for anyone else's future benefit).

XmlDocument xmlDoc = new XmlDocument();

XNamespace ns0 = "http://dev1/MyWebService1.wsdl";

xmlDoc.Load(request.GetResponse().GetResponseStream());

XDocument xDoc = XDocument.Load(new XmlNodeReader(xmlDoc));                          

String result = xDoc.Descendants(ns0 + "result").First().Value;

This of course assumes I'm getting my response back from an HttpWebRequest named request.

Mat Nadrofsky
  • 8,289
  • 8
  • 49
  • 73
0

There are very good and complete answers to this question.

I'd add just out of curiosity, that an extremely simple XPath expression does the job in this particular case:

    normalize-space(/)

This is easily done in C# using something like the two lines below:

        XPathNavigator navigator = document.CreateNavigator();

        string res = (string)navigator.Evaluate("normalize-space(/)");

With the good optimization of the .NET XPath engine, its evaluation may even be efficient.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431