Whitespace的WCF JSON反序列化问题

时间:2011-12-28 21:52:23

标签: c# wcf json rest whitespace

我有一个WCF REST服务,它接受一个自定义DataContract参数作为JSON,可以是超类型或子类型。当我传递包含额外空格的JSON时,该对象总是反序列化为超类型。当我从JSON中删除所有空格时,对象deserializaes作为子类型。

以下是一个例子:

[DataContract]
[KnownType(typeof(SubClass))]
public class SuperClass
{
    [DataMember]
    public string Message { get; set; }
}

[DataContract]
public class SubClass : SuperClass
{
    [DataMember]
    public string Extra { get; set; }
}

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [WebInvoke]
    void LogMessage(SuperClass arg);
}

public class Service1 : IService1
{
    public void LogMessage(SuperClass arg)
    {
        if (arg is SubClass)
        {
            Debug.WriteLine("SubClass");
        }
        else if (arg is SuperClass)
        {
            Debug.WriteLine("SuperClass");
        }
    }
}

如果我发送以下消息,该服务将打印SuperClass

POST http://localhost:5763/Service1.svc/LogMessage HTTP/1.1
User-Agent: Fiddler
Content-Type: text/json
Host: localhost:5763
Content-Length: 86

{    "__type":"SubClass:#WhitespaceTest",    "Message":"Message",    "Extra":"Extra" }

如果我“打印”数据包,那么我得到相同的结果,以便JSOn分成多行。但是,如果我按如下方式删除空格,服务将打印SubClass

POST http://localhost:5763/Service1.svc/LogMessage HTTP/1.1
User-Agent: Fiddler
Content-Type: text/json
Host: localhost:5763
Content-Length: 73

{"__type":"SubClass:#WhitespaceTest","Message":"Message","Extra":"Extra"}

我调试了System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString()的输出,并注意到JSON生成的XML在2个数据包之间有所不同:

<!-- First packet, deserializes to SuperClass -->
<root type="object">
    <__type type="string">SubClass:#WhitespaceTest</__type>
    <Message type="string">Message</Message>
    <Extra type="string">Extra</Extra>
</root>

<!-- Second packet, deserializes to SubClass -->
<root type="object" __type="SubClass:#WhitespaceTest">
    <Message type="string">Message</Message> 
    <Extra type="string">Extra</Extra> 
</root>

因此,似乎空白混淆了JSON反序列化器。有谁知道为什么会发生这种情况以及我能做些什么呢?

2 个答案:

答案 0 :(得分:3)

不幸的是,我对此没有好的答案。我们遇到了同样的问题,并就此与微软联系。他们坚持认为他们所做的事情是可以接受的,因为考虑到整个管道是如何运作的,这是可笑的。

所以我们的经验是,很多地方的空白都会引起问题。此外,如果您将__type字段放在除第一个之外的任何位置,您将获得超类型。

我的建议是查看JSON的其他解决方案。不幸的是,我没有选择替代方案,或者我会提出一些建议。

编辑:正如carlosfigueira指出的那样,这显然已在4.5中得到修复。我还没有尝试过它。

答案 1 :(得分:3)

这是一个已知问题,已在4.5框架中修复。不幸的是,如果要在当前框架版本中使用多态,则基本上需要从对象的前面剥离空白区域。一种方法是使用由JsonReaderWriterFactory创建的读取器/写入器来读取/写入JSON,因为它将剥离元素周围的空白区域(或任何漂亮的打印)。

public class StackOverflow_8661714
{
    [DataContract(Name = "SuperClass", Namespace = "WhitespaceTest")]
    [KnownType(typeof(SubClass))]
    public class SuperClass
    {
        [DataMember]
        public string Message { get; set; }
    }

    [DataContract(Name = "SubClass", Namespace = "WhitespaceTest")]
    public class SubClass : SuperClass
    {
        [DataMember]
        public string Extra { get; set; }
    }
    public static void Test()
    {
        string originalJson = "{    \"__type\":\"SubClass:WhitespaceTest\",    \"Message\":\"Message\",    \"Extra\":\"Extra\" }";
        byte[] originalJsonBytes = Encoding.UTF8.GetBytes(originalJson);
        MemoryStream writeStream = new MemoryStream();
        XmlDictionaryWriter jsonWriter = JsonReaderWriterFactory.CreateJsonWriter(writeStream, Encoding.UTF8, false);
        XmlDictionaryReader jsonReader = JsonReaderWriterFactory.CreateJsonReader(originalJsonBytes, 0, originalJsonBytes.Length, XmlDictionaryReaderQuotas.Max);
        jsonWriter.WriteNode(jsonReader, true);
        jsonWriter.Flush();
        Console.WriteLine(Encoding.UTF8.GetString(writeStream.ToArray()));
        writeStream.Position = 0;

        DataContractJsonSerializer dcjs = new DataContractJsonSerializer(typeof(SuperClass), new Type[] { typeof(SubClass) });
        object o = dcjs.ReadObject(writeStream);
        Console.WriteLine(o);
    }
}