c# parse soap response for attribute values and if child node exists

Gerald Oakham 21 Reputation points
2021-05-28T10:01:09.287+00:00

Hello,

I get the following details when sending data to a web service, and I am trying (in vain) to a) grab the echotoken and timestamp attribute values, and b) check that the <success /> node is there.

<?xml version='1.0' encoding='UTF-8'?>
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
   <soap-env:Header xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing">
        <wsa:ReplyTo>
            <wsa:Address>
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous
</wsa:Address>
        </wsa:ReplyTo>
        <wsa:From>urn:Mydetails</wsa:From>
        <wsa:To>
http://someURL.com/
</wsa:To>
        <wsa:Action>OTA_NotifRQ</wsa:Action>
    </soap-env:Header>
    <soap-env:Body>
    <soap-env:OTA_NotifRQ xmlns="http://www.anotherURL.org/OTA/2003/05" 
      echotoken="echo-20210517174922511"   version="1.0"    timestamp="2021-05-17T17:49:22.0000000+00:00" >
      <success />
    </soap-env:OTA_NotifRQ>
     </soap-env:Body>
</soap-env:Envelope>

Could someone please advise what I need to do ?
I have tried using XmlNamespaceManager and XmlNodeList, and using Xdocument (with Desecendants), but I get no data back.

Thanking you in advance

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
6,952 questions
No comments
{count} votes

2 answers

Sort by: Most helpful
  1. Michael Taylor 37,866 Reputation points
    2021-05-28T14:28:10.107+00:00

    The correct approach in VS is to create a service reference using the Add Service Reference in the project. This will auto-generate the necessary SOAP client code to read the XML. This is dramatically simpler than trying to manually parse a SOAP message which can be really complex, depending upon the service.

    But if I needed a super simple solution for a demo or something I would either use simple string parsing or perhaps XDocument and XPath. The challenge is with XML namespaces, they are annoying to work with. Here's working code.

    using (var reader = XmlReader.Create("test.xml"))
    {
        var root = XElement.Load(reader);
        var ns = new XmlNamespaceManager(reader.NameTable);
    
        ns.AddNamespace("soap", "http://schemas.xmlsoap.org/soap/envelope/");
        var results = root.XPathSelectElement("//soap:OTA_NotifRQ", ns);
        if (results != null)                
        {
            //Grab the namespace from the element 
            var resultsNs = results.GetDefaultNamespace();
            if (results.Element(XName.Get("success", resultsNs.NamespaceName)) != null)
            {
                var token = results.Attribute("echotoken")?.Value;
            };
        };
    };
    

    Here's what it does.

    1. Load the XML and get the namespaces defined in it.
    2. Create a new dummy namespace that lines up with the namespace that SOAP uses.
    3. Use XPath to find the desired element using the dummy namespace name.
    4. If found then get the default namespace of this element.
    5. Find the first element in the results called 'success' taking the default namespace name of the element into account.
    6. If all that was successful then grab the desired attribute(s).

    It is important to understand that for XDocument and XPath the XML names are fully qualified as namespace names, the actual prefix doesn't matter. It is simply an aliasing. So what the XML file uses to represent the SOAP namespace doesn't matter, but is still needed in order to map the SOAP element to a fully qualified namespace name. When you query XPath you simply have to ensure you are using the same fully qualified namespace name (hence the dummy namespace that was added).

    No comments

  2. Yitzhak Khabinsky 19,856 Reputation points
    2021-05-28T15:35:02.123+00:00

    Hi @Gerald Oakham ,

    While handling XML it is better to use LINQ to XML API. It is available in the .Net Framework since 2007.

    Your XML has multiple namespaces. It is relatively easy to deal with them.

    Please see below a minimal reproducible example.

    c#

    void Main()  
    {  
    	const string FILENAME = @"e:\Temp\SoapResponse.xml";  
    	XDocument xdoc = XDocument.Load(FILENAME);  
    	  
    	// Attributes handling  
    	XNamespace ns = xdoc.Root.GetNamespaceOfPrefix("soap-env");  
    	XElement xelem = xdoc.Descendants(ns + "OTA_NotifRQ").FirstOrDefault();  
      
    	string echotoken = xelem.Attribute("echotoken").Value;  
    	string timestamp = xelem.Attribute("timestamp").Value;  
    	Console.WriteLine("echotoken: '{0}', timestamp '{1}'", echotoken, timestamp);  
      
    	// <success/> element handling  
    	XNamespace ns1 = xelem.GetDefaultNamespace();  
    	bool success = xelem.Elements(ns1 + "success").Any();  
    	Console.WriteLine("Element <success> existence: {0}", success);  
    }  
    

    Output

    echotoken: 'echo-20210517174922511', timestamp '2021-05-17T17:49:22.0000000+00:00'  
    Element <success> existence: True