Scott Salyer
Scott Salyer

Reputation: 2475

Deserializing WCF Message at Server

I implemented a custom message inspector (via IDispatchMessageInspector) to intercept messages that are received on the server side of a WCF service so I can attempt to deserialize the message and apply some specific business logic. The problem I'm encountering is when I write the MessageBuffer's contents to a new MemoryStream and then try to deserialize, I get an error that says "The data at the root level is invalid. Line 1, position 1." I do know the data being passed in is valid as skipping over the inspector makes everything work fine.

Sample Code:

public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request, System.ServiceModel.IClientChannel channel, System.ServiceModel.InstanceContext instanceContext)
    {
        MessageBuffer buffer = request.CreateBufferedCopy(Int32.MaxValue);
        request = buffer.CreateMessage();
        string msg = buffer.CreateMessage().ToString();

        var dc = new DataContractSerializer(typeof(Adder));

        using (var stream = new MemoryStream())
        {
            buffer.WriteMessage(stream);

            stream.Position = 0;

            //deserializing error occurs here
            var c = dc.ReadObject(stream);
        }

        return null;
    }

Here is the Adder class/interface:

    [DataContract(Name = "adder", Namespace = "http://test.com")]
public class Adder
{
    [DataMember(Name = "first")]
    public int First { get; set; }

    [DataMember(Name = "second")]
    public int Second { get; set; }
}

    [ServiceContract(Namespace = "http://test.com")]
public interface ITestSvc
{
    [OperationContract(Name = "add")]
    int Add(Adder adder);
}

Any suggestions or is there a better option for this? My main goal is to read the XML (in a deserialized object) on every WCF request that comes into my service.

Upvotes: 0

Views: 6378

Answers (3)

user3182856
user3182856

Reputation: 1

I just did this. Just paste all of the following code into your class, and call DeserializedResponse(). You'll need to change MyResponseObject to the name of whatever object you are trying to deserialize. Also, you will need to replace "_requestInspector.Response" with your own response xml string variable. The method GetSoapBodyInnerXml() will strip off the Soap Envelope, and only return your response xml which you wish to deserialize.

    private MyResponseObject DeserializedResponse()
    {
        var rootAttribute = new XmlRootAttribute("MyResponseObject ");
        rootAttribute.Namespace = @"http://www.company.com/MyResponseObjectNamespace";

        XmlSerializer serializer = new XmlSerializer(typeof(MyResponseObject ), rootAttribute);
        string responseSoapBodyInnerXml = GetSoapBodyInnerXml(_requestInspector.Response);
        AddXmlDeclaration(ref responseSoapBodyInnerXml);
        MemoryStream memStream = new MemoryStream(Encoding.UTF8.GetBytes(responseSoapBodyInnerXml));
        MyResponseObject resultingResponse = (MyResponseObject )serializer.Deserialize(memStream);

        return resultingResponse;
    }

    private string GetSoapBodyInnerXml(string soapMessage)
    {
        XDocument xDoc = XDocument.Parse(soapMessage);
        XNamespace nsSoap = @"http://schemas.xmlsoap.org/soap/envelope/";
        return xDoc.Descendants(nsSoap + CONST_SoapBody).Descendants().First().ToString();
    }

    private void AddXmlDeclaration(ref string xmlString)
    {
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(xmlString);

        //Create an XML declaration. 
        XmlDeclaration xmldecl;
        xmldecl = doc.CreateXmlDeclaration("1.0", "UTF-8", null);

        //Add the new node to the document.
        XmlElement root = doc.DocumentElement;
        doc.InsertBefore(xmldecl, root);

        //Return updated xmlString with XML Declaration
        xmlString = doc.InnerXml;
    }

Upvotes: 0

Scott Salyer
Scott Salyer

Reputation: 2475

I ended up going the message header route, like Mike Parkhill suggested.

Upvotes: -3

Mike Parkhill
Mike Parkhill

Reputation: 5551

The request object contains the WCF message headers as well as the payload. You'll need to strip off the headers and then you should be able to deserialize the message body.

For example, a SOAP message would have:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" 
               xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Header>

</soap:Header>
<soap:Body>
  <!-- your payload -->
</soap:Body>

You could use XML navigation to get to the body element and then you'd deserialize that element alone.

EDIT: Actually just stumbled on this method which I think should do the trick for you:

Message.GetReaderAtBodyContents

Upvotes: 5

Related Questions