Why can't I deserialize my object if the xml it contains gets slightly larger?

有些话、适合烂在心里 提交于 2019-12-11 12:52:24

问题


I am having an issue trying to send a message to the Azure service bus using the REST API and have it received using the .NET azure service bus client API classes. I can happily send and receive messages using these objects using the SDK alone, but get problems when trying to use the REST api.

I have narrowed the issue down to this minimal repro I think. Basically if I serialize my object with a small amoutn of XML as the payload thee everything works ok. If I add slightly more XML then I get an exception deserializing it. this should cut&paste into a new console application to allow repro of the issue:

using System.IO;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Xml;
using System.Xml.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main(string[] args)
    {
        GetMessageBytes(Guid.NewGuid());
    }

    private static byte[] GetMessageBytes(Guid requestId)
    {
        var payload = XElement.Parse(@"
<blah Type='TransactionRequest' Version='1' xmlns=''>
    <SomeData Type='Request' Selection='All'></SomeData>
</blah>");

        var invalidPayload = XElement.Parse(@"
<blah Type='TransactionRequest' Version='1' xmlns=''>
    <SomeData Type='Request' Selection='All'></SomeData>
    <SomeData Type='Request' Selection='All'></SomeData>
    <SomeData Type='Request' Selection='All'></SomeData>
    <SomeData Type='Request' Selection='All'></SomeData>
    <SomeData Type='Request' Selection='All'></SomeData>
</blah>");
        Message<XElement> message = new Message<XElement>()
        {
            Label = "Label",
            RequestId = requestId,
            Payload = payload
        };

        var messageBytes = EncodeMessage(message);
        var expectedResponse = DecodeMessage<Message<XElement>>(messageBytes);

        message = new Message<XElement>()
        {
            Label = "Label",
            RequestId = requestId,
            Payload = invalidPayload
        };

        messageBytes = EncodeMessage(message);
        expectedResponse = DecodeMessage<Message<XElement>>(messageBytes);
        Console.WriteLine(expectedResponse);
        return messageBytes;
    }

    private static byte[] EncodeMessage<T>(T message)
    {
        DataContractSerializer serializer = new DataContractSerializer(typeof(T));
        var memoryStream = new MemoryStream();
        XmlDictionaryWriter binaryDictionaryWriter = XmlDictionaryWriter.CreateBinaryWriter(memoryStream);
        serializer.WriteObject(binaryDictionaryWriter, message);
        binaryDictionaryWriter.Flush();
        var bytesToPost = memoryStream.GetBuffer();
        return bytesToPost;
    }

    private static T DecodeMessage<T>(byte[] response)
    {
        var ms = new MemoryStream(response);
        var serializer = new DataContractSerializer(typeof(T));
        XmlDictionaryReader binaryDictionaryReader = XmlDictionaryReader.CreateBinaryReader(ms, XmlDictionaryReaderQuotas.Max);

        var message = serializer.ReadObject(binaryDictionaryReader);
        return (T)message;
    }
}

[MessageContract(WrapperName = "Message", WrapperNamespace = "http://blah.co.uk/contracts", IsWrapped = true)]
public sealed class Message<T>        where T : class
{
    [MessageHeader(Namespace = "http://blah.co.uk/contracts", Name = "RequestId")]
    public Guid RequestId { get; set; }

    [MessageHeader(Namespace = "http://blah.co.uk/contracts", Name = "Label")]
    public string Label { get; set; }

    [MessageBodyMember(Namespace = "http://blah.co.uk/contracts", Name = "Payload")]
    public T Payload { get; set; }

    [MessageBodyMember(Namespace = "http://blah.co.uk/contracts", Name = "MonitoringResults")]
    public MessageTimestamp MessageTimestamp { get; set; }


}

[DataContract]
public class MessageTimestamp
{
    private ICollection<Timestamp> timestamps = new List<Timestamp>();

    [DataMember]
    public ICollection<Timestamp> GetAllTimestamps
    {
        get
        {
            return timestamps.ToList();
        }

        private set
        {
            timestamps = new List<Timestamp>(value);
        }
    }        
}

public class Timestamp
{
    public Operation Operation { get; set; }
    public DateTime StartTime { get; set; }
    public DateTime EndTime { get; set; }

    public TimeSpan TimeTaken
    {
        get
        {
            return EndTime - StartTime;
        }
    }
}

public enum Operation
{
    OverallProcessing
}

The error that is thrown is:

There was an error deserializing the object of type SerialisationIssue.Message1[[System.Xml.Linq.XElement, System.Xml.Linq, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]`. The input source is not correctly formatted.

I have tried serialising large amounts of just XML (ie no Message<XElement> wrapper) and that works ok, so I'm sure its not actually related to the size of the XML, but that consistently make it break.

I've reflected the actual class that is used in the ServiceBus SDK client libraries and tried to use the deserialiser found in there (DataContractBinarySerializer) but that has made no difference and seems to basically do the same thing that I'm doing manually anyway.

Any ideas on how I can find what the actual problem is? And how I might go about fixing it?


回答1:


So the issue is indeed related to some apparent bug in the XmlBinaryReader as outlined in this connect issue

which can be worked around by writing some whitespace at the end of the serialized data, as suggested in this post



来源:https://stackoverflow.com/questions/22152085/why-cant-i-deserialize-my-object-if-the-xml-it-contains-gets-slightly-larger

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!