16

I want to extract SOAP body from a SOAP message, I have some data in SOAP body that I have to parse in date base, so this is the code:

public string Load_XML(string SoapMessage)
{
    //check soap message
    if (SoapMessage == null || SoapMessage.Length <= 0)
        throw new Exception("Soap message not valid");

    //declare some local variable
    int iSoapBodyStartIndex = 0;
    int iSoapBodyEndIndex = 0;

    //load the Soap Message
    //Učitaj string XML-a i pretvori ga u XML
    XmlDocument doc = new XmlDocument();

    try
    {
        doc.Load(SoapMessage);
    }

    catch (XmlException ex)
    {
        WriteErrors.WriteToLogFile("WS.LOAD_DOK_LoadXML", ex.ToString());

        throw ex;
    }

    //search for the "http://schemas.xmlsoap.org/soap/envelope/" URI prefix
    string prefix = string.Empty;
    for (int i = 0; i < doc.ChildNodes.Count; i++)
    {
        System.Xml.XmlNode soapNode = doc.ChildNodes[i];
        prefix = soapNode.GetPrefixOfNamespace("http://schemas.xmlsoap.org  /soap/envelope/");

        if (prefix != null && prefix.Length > 0)
            break;
    }

    //prefix not founded. 
    if (prefix == null || prefix.Length <= 0)
        throw new Exception("Can't found the soap envelope prefix");

    //find soap body start index
    int iSoapBodyElementStartFrom = SoapMessage.IndexOf("<" + prefix + ":Body");
    int iSoapBodyElementStartEnd = SoapMessage.IndexOf(">", iSoapBodyElementStartFrom);    -> HERE I HAVE AN ERROR!!!!   
    iSoapBodyStartIndex = iSoapBodyElementStartEnd + 1;

    //find soap body end index
    iSoapBodyEndIndex = SoapMessage.IndexOf("</" + prefix + ":Body>") - 1;

    //get soap body (xml data)
    return SoapMessage.Substring(iSoapBodyStartIndex, iSoapBodyEndIndex - iSoapBodyStartIndex + 1);
}

I got an error here:

int iSoapBodyElementStartEnd = SoapMessage.IndexOf(">", iSoapBodyElementStartFrom); 

The error:

Index was out of range. Must be non-negative and less than the size of the collection.

If anyone knows how to solve this?

Luke Girvin
  • 13,221
  • 9
  • 64
  • 84
CrBruno
  • 963
  • 8
  • 18
  • 33
  • Is it definitely non-negative? I'd guess it's -1 because the start block wasn't matched in the string. What's in the string? – Rup Apr 24 '12 at 08:46
  • I'd also check would be whether `prefix` is read correctly. Your SOAP namespace shouldn't have spaces in the middle - does removing those help? It may also be better to use a proper XML parser here rather than substring matching. – Rup Apr 24 '12 at 08:51
  • What do you mean by proper XML parser? I'm not folowing you? This is what i want to read 1234 – CrBruno Apr 24 '12 at 11:33
  • I meant use `soapNode` to find the body tag and extract the content you want rather than using a substring match. Looking at your example, you've got lower-case-b 'body' in the XML but upper-case 'Body' in the code. – Rup Apr 24 '12 at 11:36
  • Well thats the problem, I don't now how to write the code, can you help me, I'm going mad about it! – CrBruno Apr 24 '12 at 11:44

4 Answers4

14

For a request like this:

String request = @"<?xml version=""1.0"" encoding=""UTF-8""?>
    <soap:Envelope xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""
    xmlns:soapenc=""http://schemas.xmlsoap.org/soap/encoding/""
    xmlns:xsd=""http://www.w3.org/2001/XMLSchema""
    xmlns:soap=""http://schemas.xmlsoap.org/soap/envelope/"">
    <soap:Body>
    <ResponseData xmlns=""urn:Custom"">some data</ResponseData>
    </soap:Body>
    </soap:Envelope>";

The following code did the work to unwrap the data and get only the <ReponseData> xml content:

XDocument xDoc = XDocument.Load(new StringReader(request));

var unwrappedResponse = xDoc.Descendants((XNamespace)"http://schemas.xmlsoap.org/soap/envelope/" + "Body")
    .First()
    .FirstNode
Luis Quijada
  • 2,345
  • 1
  • 26
  • 31
  • This does not work generally. When using another soap definition like "http://www.w3.org/2003/05/soap-envelope", this will not find a body – Thoker Jul 06 '22 at 09:29
  • Use the namespace of the envelope to always extract the correct soap body, otherwise this only works for the hard coded soap schema var unwrappedResponse = xDoc.Descendants((XNamespace) xDoc.Root.Name.Namespace.NamespaceName + "Body") .First() .FirstNode – Thoker Jul 06 '22 at 10:06
12

Linq2Xml is simpler to use.

string xml = @"<?xml version=""1.0"" encoding=""UTF-8"" ?>
    <soap:envelope xmlns:xsd=""w3.org/2001/XMLSchema"" xmlns:xsi=""w3.org/2001/XMLSchema-instance"" xmlns:soap=""schemas.xmlsoap.org/soap/envelope/"">; 
    <soap:body> 
    <order> <id>1234</id>  </order> 
    </soap:body> 
    </soap:envelope>";

XDocument xDoc = XDocument.Load(new StringReader(xml));
var id =  xDoc.Descendants("id").First().Value;

--EDIT--

To loop elements in body:

XDocument xDoc = XDocument.Load(new StringReader(xml));
XNamespace soap = XNamespace.Get("schemas.xmlsoap.org/soap/envelope/");

var items = xDoc.Descendants(soap+"body").Elements();
foreach (var item in items)
{
    Console.WriteLine(item.Name.LocalName);
}
L.B
  • 114,136
  • 19
  • 178
  • 224
  • O thanks, just a one little question, if I want to extract all of elements in body segment, not just first and last, how to write it? – CrBruno Apr 25 '12 at 07:36
2

You can utilize GetElementsByTagName to extract the body of the soap request.

private static T DeserializeInnerSoapObject<T>(string soapResponse)
{
    XmlDocument xmlDocument = new XmlDocument();
    xmlDocument.LoadXml(soapResponse);

    var soapBody = xmlDocument.GetElementsByTagName("soap:Body")[0];
    string innerObject = soapBody.InnerXml;

    XmlSerializer deserializer = new XmlSerializer(typeof(T));

    using (StringReader reader = new StringReader(innerObject))
    {
        return (T)deserializer.Deserialize(reader);
    }
}
Tunaki
  • 132,869
  • 46
  • 340
  • 423
Rich Hildebrand
  • 1,607
  • 17
  • 15
  • this only works, when the prefix is soap in the whole message. would not work for the following message: hello – Thoker Jul 06 '22 at 08:53
0

Simple solution, if body has multiple children, and you just wat to remove envelope:

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(placeYourXmlHere);

    if (xmlDoc.DocumentElement.Name == "soapenv_Envelope")
    {
        string tempXmlString = xmlDoc.DocumentElement.InnerXml;
        xmlDoc.LoadXml(tempXmlString);
    }

If you want to remove both Envelope and body, where body only contain one child:

    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(placeYourXmlHere);

    while (xmlDoc.DocumentElement.Name == "soapenv_Envelope" || xmlDoc.DocumentElement.Name == "soapenv_Body")
    {
        string tempXmlString = xmlDoc.DocumentElement.InnerXml;
        xmlDoc.LoadXml(tempXmlString);
    }

Now the xml is reduced to the content of the Body

Gywerd
  • 1
  • 1