-->

How to deserialize a WCF soap response message fro

2020-08-17 18:11发布

问题:

When I call a web service operation, WCF deserializes the message to the proxy class with the DataContractSerializer: why couldn't I do the same ?

Here is the soap message in the file ActLoginResponse.xml:

<SOAP-ENV:Envelope xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="urn:PlotiIntf" xmlns:ns2="urn:PlotiIntf-IPloti" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"/>
    <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
        <ns2:ActLoginResponse>
            <return>
                <ResultCode>0</ResultCode>
                <ResultMessage>Login et password correct.</ResultMessage>
                <Acteur>
                    <Id>IMT_706</Id>
                    <Nom>IMA PROTECT</Nom>
                    <Prenom/>
                    <nbFI>0</nbFI>
                    <FonctionActeur>TS</FonctionActeur>
                    <Timeout>30</Timeout>
                </Acteur>
                <ZneGeoList xsi:nil="true"/>
            </return>
        </ns2:ActLoginResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The WCF proxy code for the corresponding ActLoginResponse class is :

[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
[System.ServiceModel.MessageContractAttribute(WrapperName="ActLoginResponse", WrapperNamespace="urn:PlotiIntf-IPloti", IsWrapped=true)]
public partial class ActLoginResponse {

    [System.ServiceModel.MessageBodyMemberAttribute(Namespace="", Order=0)]
    public Ploti.PlotiClient.LoginResponseType @return;

    public ActLoginResponse() {
    }

    public ActLoginResponse(Ploti.PlotiClient.LoginResponseType @return) {
        this.@return = @return;
    }
}

So I need to parse the XML to an object instance of type ActLoginResponse.

Here is how I perform the parsing:

        ActLoginResponse body;
        FileStream stream = new FileStream("Requests\\ActLoginResponse.xml", FileMode.Open);
        XmlReader xmlReader = XmlReader.Create(stream);

        xmlReader.MoveToContent();
        xmlReader.ReadStartElement();
        xmlReader.MoveToContent();
        xmlReader.ReadStartElement();
        xmlReader.MoveToContent();
        xmlReader.ReadStartElement();
        xmlReader.MoveToContent();

        // the the reader is on the element ActLoginResponse (that is confirmed by a Log.Debug( xmlReader.ReadOuterXml());

        // I create The DataContractSerializer: exception if nampespace is not specified
        DataContractSerializer dataContract = new `DataContractSerializer`(typeof(ActLoginResponse), "ActLoginResponse", "urn:PlotiIntf-IPloti");

        ActLoginResponse actLogin = dataContract.ReadObject(xmlReader, true);

The actLogin object is created, but the content actLogin.return is allways NULL ! Did I miss something ?

回答1:

I used the WSDL you had provided from the other question and created a proxy class from it.

Using the above XML, I seem to have no issues deserializing into the ActLoginResponse by the following:

Message m = Message.CreateMessage(XmlReader.Create("C:\\testSvc\\login.xml"), int.MaxValue, MessageVersion.Soap11);
SoapReflectionImporter importer = new SoapReflectionImporter(new SoapAttributeOverrides(), "urn:PlotiIntf-IPloti");
XmlTypeMapping mapp = importer.ImportTypeMapping(typeof(ActLoginResponse));
XmlSerializer xmlSerializer = new XmlSerializer(mapp); //typeof(T), xr
var o = (ActLoginResponse)xmlSerializer.Deserialize(m.GetReaderAtBodyContents());

reference



回答2:

You should be able to use XmlSerializer to accomplish this with help of SoapReflectionImporter.

var importer = new SoapReflectionImporter("urn:PlotiIntf-IPloti");
var mapping = importer.ImportTypeMapping(typeof(ActLoginResponse));
var serializer = new XmlSerializer(mapping);
var response = serializer.Deserialize(reader) as ActLoginResponse;

The SoapReflectionImporter class provides type mappings to SOAP-encoded message parts, as defined in a Web Services Description Language (WSDL) document. It is used only when a Web service or client specifies SOAP encoding, as described in Section 5 of the SOAP 1.1 specification.

The following command was used to generate your client contracts from WSDL

SvcUtil.exe IPlotiservice.wsdl /t:code /serviceContract


回答3:

This is an old one, but it came up as my first search on Google for how to deserialize an object, so I thought it would be worthwhile to post the solution that worked for me in .NET Core.

In our case, we generated the code from the WSDL using the svcutil tool, and then used XmlSerializer, but it was throwing an invalid operation exception. The fix was to add the XmlRoot attribute to our object (that is, the first element of the Soap Envelope).

So we had to decorate the object like this:

    [System.Xml.Serialization.XmlRoot(ElementName = "Foo", Namespace = "ns")]
    public partial class FooType
    {
    }

And then we could deserialize like so:

            using var stream = new MemoryStream(Encoding.UTF8.GetBytes(/* stream object */));
            using var message = Message.CreateMessage(XmlReader.Create(stream), int.MaxValue, MessageVersion.Soap11);
            using var xmlStream = message.GetReaderAtBodyContents();

            var serializer = new XmlSerializer(typeof(FooType));
            var obj = serializer.Deserialize(xmlStream);

Hopefully this helps others :)