0

Following is the XML from which I am trying to extract a child element.

<?xml version="1.0" encoding="UTF8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Header xmlns="http://SomeValue/SomeValue/2009/01/SomeValue.xsd">
<Session>
<SessionId>SomeValue</SessionId>
<SequenceNumber>1</SequenceNumber>
<SecurityToken>SomeValue</SecurityToken>
</Session>
</soap:Header>
<soap:Body>
<Security_AuthenticateReply xmlns="http://SomeValue/SomeValue">
<processStatus>
<statusCode>P</statusCode>
</processStatus>
</Security_AuthenticateReply>
</soap:Body>
</soap:Envelope>
public static string AssignSecurityToken(string response)
{
        string Token = "";

        XNamespace ns = "http://schemas.xmlsoap.org/soap/envelope/";
        XElement xdoc = XElement.Parse(response);
        XElement root = xdoc.Descendants(ns + "Header").First();

        Token = root.Element("Session").Element("SecurityToken").Value;

        Token = root.Descendants("Session").Descendants().Where(n => n.Name == "SecurityToken").FirstOrDefault().Value;
        return Token;
}

I want to extract the element Security Token.

Following are the things that I have already worked on:

  • Tried extracting the element using the approach suggested in the post How to get value of child node from XDocument

  • Also posting some code for reference. Both the statements that are assigning values to the Token variable are throwing "Object not set to an instance of an object"exception.

Thanks in advance.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
Gurunath Rao
  • 105
  • 1
  • 8
  • 2
    That looks like an Amadeus response. Why are you trying to parse the SOAP response yourself instead of using eg WCF? – Panagiotis Kanavos Nov 05 '19 at 14:55
  • You got me with Amadeus! Well, I am trying to extract the Security Token and put it in the SOAP Header to send a request for one of the Enterprise API Operations...its part of an upgrade to SOAP 4.0 from my legacy code. – Gurunath Rao Nov 05 '19 at 15:02

3 Answers3

2

You need to take into account the Header's namepace.

public static string AssignSecurityToken(string response)
{
    XNamespace ns1 = "http://schemas.xmlsoap.org/soap/envelope/";
    XNamespace ns2 = "http://SomeValue/SomeValue/2009/01/SomeValue.xsd";

    var envelope = XElement.Parse(response);
    var header = envelope.Element(ns1 + "Header");
    var session = header.Element(ns2 + "Session");
    var security_token = session.Element(ns2 + "SecurityToken");

    return security_token.Value;
}

Actually you could go ahead and just call

return XElement.Parse(response).Descendants()
            .First(x => x.Name.LocalName == "SecurityToken").Value;
Innat3
  • 3,561
  • 2
  • 11
  • 29
1

For this response only, it makes sense to just parse the string and extract an element. This response uses two namespaces, one for SOAP headers and another for the Amadeus login response. You need the second one to retrieve the token :

//SOAP-only namespace
XNamespace soap = "http://schemas.xmlsoap.org/soap/envelope/";
//Default namespace
XNamespace ns = "http://SomeValue/SomeValue/2009/01/SomeValue.xsd";

var response=XElement.Parse(xml);
var token=response.Descendants(ns+"SecurityToken").First().Value;

Other Amadeus responses are huge and XDocument won't be much better (if at all) than using WCF and deserializing to strongly typed objects. XDocument deserializes the entire XML response, the same as DataContractSerializer. Instead of getting back a strongly-typed set of objects though, you get XElements you'll have to map to something else.

If you want to reduce memory consumption by only reading the parts you'll have to use XmlReader and read the XML tokens from the response stream one by one. That's a lot more work.

Another interesting thing is that Amadeus responses use multiple namespaces. This response uses just 2. Other responses, eg searches, use many more.

Panagiotis Kanavos
  • 120,703
  • 13
  • 188
  • 236
  • Thanks for the advice! I am already using the strongly typed classes approach for deserializing the response. Also, this request is having fewer namespaces because it is being used for fetching few security credentials which in turn will be used for another process down the line. – Gurunath Rao Nov 05 '19 at 16:34
0

You might consider working with System.Xml.XmlDocument and System.Xml.XPath.XPathNavigator which are really easy to work with.

I wrote a simple example for you (supporting UTF-8 encoding):

System.Xml.XmlDocument someXmlFile = new System.Xml.XmlDocument();
string xmlPath= @"YOUR_XML_FULL_PATH";
string innerNodeToExtract= @"/Session/SecurityToken";

try
{
    // Loading xml file with utf-8 encoding:
    string xmlFileStr= Systm.IO.File.ReadAllText(xmlPath, System.Text.Encoding.UTF8);

    // Creating the XPathNavigator:
    System.Xml.XPath.XPathNavigator xmlNavigator= someXmlFile.CreateNavigator();

    // Getting the inner value as string:
    string value = xmlNavigator.SelectSingleNode(innerNodeToExtract).Value;

    // some code...
}
catch(Exception) 
{
    // Handle failures
}

Please notice that you can also:

  • Extract inner values using "@" key.

  • Move to the child using xmlNavigator.MoveToNext().

And many other things that you can read here.

LiadPas
  • 16