Wrap XML in SOAP envelope in .net

36,686

Solution 1

I was able to solve this by using an XLST to wrap the XML in soap.

Here's my working test app code

using System;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;
using System.Xml.Xsl;


namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            GetBasicData getBasicData = new GetBasicData();
            getBasicData.CONO = "1";
            getBasicData.CUNO = "201702";

            XPathDocument requestXPathDocument;

            if (SerializeIntoRequestXPathDocument(getBasicData, out requestXPathDocument))
            {
                XmlDocument requestXmlDocument;

                if (CreateRequestXMLDocument(requestXPathDocument, @"Z:\Darice\M3 SOAP\GetBasicData.xsl", out requestXmlDocument))
                {
                    XmlDocument responseXmlDocument;

                    if (ExecuteRequestSoap(requestXmlDocument, out responseXmlDocument))
                    {
                        MemoryStream unwrappedMemoryStream;

                        if (UnwrapSoapResponseXmlDocumentIntoMemoryStream(responseXmlDocument, out unwrappedMemoryStream))
                        {
                            GetBasicDataResponse getBasicDataResponse;

                            if (!DeserializeResponseMemoryStream(unwrappedMemoryStream, out getBasicDataResponse))
                            {
                                Debug.WriteLine("FAIL");
                            }
                        }
                    }
                }
            }

            Console.ReadLine();
        }


        //STATIC FUNCTIONS
        private static Boolean CreateRequestXMLDocument(XPathDocument xPathDocument, String xslPath, out XmlDocument xmlDocument)
        {
            try
            {
                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(memoryStream))
                    {
                        XmlWriter xmlWriter = XmlWriter.Create(streamWriter);

                        XsltSettings xsltSettings = new XsltSettings();
                        xsltSettings.EnableScript = true;

                        XslCompiledTransform xslCompiledTransform = new XslCompiledTransform();
                        xslCompiledTransform.Load(xslPath, xsltSettings, null);
                        xslCompiledTransform.Transform(xPathDocument, xmlWriter);

                        memoryStream.Position = 0;

                        using (StreamReader streamReader = new StreamReader(memoryStream))
                        {
                            XmlReader xmlReader = XmlReader.Create(streamReader);

                            xmlDocument = new XmlDocument();
                            xmlDocument.Load(xmlReader);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                xmlDocument = null;

                return false;
            }

            return true;
        }

        private static Boolean DeserializeResponseMemoryStream<T>(MemoryStream memoryStream, out T xmlObject)
        {
            try
            {
                using (StreamReader streamReader = new StreamReader(memoryStream))
                {
                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

                    using (XmlReader xmlReader = XmlReader.Create(streamReader))
                    {
                        xmlObject = (T)xmlSerializer.Deserialize(xmlReader);
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                xmlObject = default(T);

                return false;
            }

            return true;
        }

        private static Boolean ExecuteRequestSoap(XmlDocument requestXmlDocument, out XmlDocument responseXmlDocument)
        {
            try
            {
                Byte[] byteArray = UTF8Encoding.UTF8.GetBytes(requestXmlDocument.OuterXml);

                WebRequest webRequest = WebRequest.Create(Properties.Resources.SoapServerAddress);
                webRequest.ContentLength = byteArray.Length;
                webRequest.ContentType = @"text/xml; charset=utf-8";
                webRequest.Headers.Add("SOAPAction", @"http://schemas.xmlsoap.org/soap/envelope/");
                webRequest.Method = "POST";

                using (Stream requestStream = webRequest.GetRequestStream())
                {
                    requestStream.Write(byteArray, 0, byteArray.Length);

                    using (WebResponse webResponse = webRequest.GetResponse())
                    {
                        using (Stream responseStream = webResponse.GetResponseStream())
                        {
                            using (StreamReader streamReader = new StreamReader(responseStream))
                            {
                                responseXmlDocument = new XmlDocument();
                                responseXmlDocument.LoadXml(streamReader.ReadToEnd());
                            }
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                responseXmlDocument = null;

                return false;
            }

            return true;
        }

        private static Boolean SerializeIntoRequestXPathDocument<T>(T dataObject, out XPathDocument xPathDocument)
        {
            try
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

                using (MemoryStream memoryStream = new MemoryStream())
                {
                    using (StreamWriter streamWriter = new StreamWriter(memoryStream))
                    {
                        xmlSerializer.Serialize(streamWriter, dataObject);

                        memoryStream.Position = 0;

                        using (StreamReader streamReader = new StreamReader(memoryStream))
                        {
                            memoryStream.Position = 0;

                            xPathDocument = new XPathDocument(streamReader);
                        }
                    }
                }
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                xPathDocument = null;

                return false;
            }

            return true;
        }

        private static Boolean UnwrapSoapResponseXmlDocumentIntoMemoryStream(XmlDocument responseXmlDocument, out MemoryStream memoryStream)
        {
            try
            {
                XmlNamespaceManager xmlNamespaceManager = new XmlNamespaceManager(responseXmlDocument.NameTable);
                xmlNamespaceManager.AddNamespace("tns", "CRS610MI");
                xmlNamespaceManager.AddNamespace("SOAP", @"http://schemas.xmlsoap.org/soap/envelope/");

                XmlNode xmlNode = responseXmlDocument.SelectSingleNode(@"/SOAP:Envelope/SOAP:Body/tns:GetBasicDataResponse", xmlNamespaceManager);

                memoryStream = new MemoryStream(UTF8Encoding.UTF8.GetBytes(xmlNode.OuterXml));
            }
            catch (Exception exception)
            {
                Debug.WriteLine(exception);

                memoryStream = null;

                return false;
            }

            return true;
        }
    }
}

And here's the XSL code

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:crs="CRS610MI" exclude-result-prefixes="crs">
<xsl:output method="xml"/>
<xsl:template match="/">
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <soap:Body>
    <xsl:apply-templates select="node()|@*"/>
    </soap:Body>
  </soap:Envelope>
</xsl:template>
<xsl:template match="node()|@*">
    <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>
</xsl:template>
</xsl:stylesheet>

Solution 2

If the service provider does not provide a WSDL, then you should not do business with them. It's not rocket science, and it is the standard way that SOAP web services work. It's only been a standard for over a decade. I would seriously wonder in what other way this service provider is incompetent.

If you have no choice but to do business with incompetent business partners, then

  1. Good luck to you
  2. Create your own WSDL to match their service, then use "Add Service Reference" to create the proxy classes necessary to communicate to the service.
Share:
36,686
Matthew
Author by

Matthew

Updated on July 09, 2022

Comments

  • Matthew
    Matthew almost 2 years

    I need help with wrapping an XML in a SOAP envelope for a third party SOAP server. The third party has provided xsd files for the inbound request and outbound response. I've taken those XSD files and created C# classes of them using the xsd tool. My problem is that I need to wrap the serialized request with a SOAP envelope and I don't know where to start. I was looking at the Microsoft Web Service Enhancements 3, but that says that it's only for .net 2.0 and VS2005. I am using VS2012 and .net 4.5. Also, I've looked into connecting to the server by way of web service but it doesn't seem compatible and does not have a WSDL.

    The following is a sample of what the SOAP server expects for an inbound request.

    <?xml version="1.0" encoding="utf-8"?>
    <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
    <soap:Body>
    <GetBasicData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="CRS610MI">
    <CONO xmlns="">1</CONO>
    <CUNO xmlns="">12345</CUNO>
    </GetBasicData>
    </soap:Body>
    </soap:Envelope>
    

    This is what the serialized XML string looks like.

    <?xml version="1.0" encoding="utf-8"?>
    <GetBasicData xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="CRS610MI">
    <CONO xmlns="">1</CONO>
    <CUNO xmlns="">12345</CUNO>
    </GetBasicData>
    

    Code I'm using for my web request and response.

    Byte[] byteArray = System.Text.UTF8Encoding.UTF8.GetBytes(data);
    
    WebRequest webRequest = WebRequest.Create(@"http://myserver:8888");
    webRequest.ContentLength = byteArray.Length;
    webRequest.ContentType = @"text/xml; charset=utf-8";
    webRequest.Headers.Add("SOAPAction", @"http://schemas.xmlsoap.org/soap/envelope/");
    webRequest.Method = "POST";
    
    Stream requestStream = webRequest.GetRequestStream();
    requestStream.Write(byteArray, 0, byteArray.Length);
    requestStream.Close();
    requestStream.Dispose();
    
    WebResponse webResponse = webRequest.GetResponse();
    
    Stream responseStream = webResponse.GetResponseStream();
    
    StreamReader streamReader = new StreamReader(responseStream);
    
    String line;
    
    while ((line = streamReader.ReadLine()) != null)
    {
        Debug.WriteLine(line);
    }
    

    I've tested my code by replacing my serialized string with the text in the sample file provided by the third party and it worked as expected. I also took my serialized string and inserted the envelope text in the correct places and that also worked, web request went through and I got the response I was looking for. Short of inserting the envelope text into my serialized string manually what can I do. I have to imagine there's a method or class that will take care of this for me in a standardized way?

  • Matthew
    Matthew about 10 years
    Yes I realize this. This is an old service that hasn't been updated in a long time, but it still works. There's an alternative, which is calling the api directly and then parsing fields out of a huge single line string which has no documentation as to what each value is. So lesser of two evils I suppose.
  • Matthew
    Matthew about 10 years
    I tried adding the message contract and message body members to the properties, but the serialized string looks the same as it did before. I am using the XML serializer, should I be using something else?
  • TYY
    TYY about 10 years
    I'm not in front of a PC so I can't give you exacts, however if you google c# soap Message.CreateMessage will see what to do.
  • John Saunders
    John Saunders about 10 years
    Why does it need to be updated? Why didn't it have a WSDL to begin with? I mean, really, WSDL has been part of SOAP from the beginning. Maybe they just lost the WSDL? Or, if you know the service well enough to try faking up an envelope by hand, maybe you can create a WSDL that will do the same thing? Then you can give the service provider the gift of the WSDL.
  • StingyJack
    StingyJack almost 7 years
    Sometimes using the raw soap exchange is necessary when the WSDL is turning out to be yet another interop failure between Java and .NET, and you cant get the provider to change it because "it works in soapUI and eclipse".