How to deserialize a WCF soap response message from a file with DataContractSerializer?
Asked Answered
P

3

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 ?

Precipitous answered 2/5, 2017 at 15:22 Comment(5)
Do you have the properties of the LoginResponseType class defined? If not then it will not be able to deserialize the values from the xml to the object.Testudinal
Yes all properties are defined because LoginResponseType is a class generated when referencing the WSDL as a service. When I use the web service LoginResponseType is deserialized from the SOAP responses received from the network. I want to do the same bu from a file.Fine
What is a response from a wcf client webservice call ? Do you mean a WCF Service response?Landlubber
Sorry for my english, I corrected the text.Fine
can you give me your code sample for i work on it?Monandrous
T
5

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

Testudinal answered 10/5, 2017 at 19:48 Comment(2)
I realize this is an older question, but without the WSDL, it makes it very difficult to use your answer.Enmity
@WonkotheSane My answer is based off of the information provided in the OP's question. dropbox.com/s/gbdd3w3th0lon04/IPlotiservice.wsdl?dl=0Testudinal
P
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
Preussen answered 9/5, 2017 at 21:48 Comment(0)
P
2

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 :)

Pannikin answered 7/5, 2020 at 13:35 Comment(1)
Just a little improvement; I got it working without modifying the datatype. Just change the serializer to var serializer = new XmlSerializer(typeof(FooType), null, null, new XmlRootAttribute("Foo"), "ns");Boondoggle

© 2022 - 2024 — McMap. All rights reserved.