2011-12-28 93 views
2

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

下面是一个例子:

[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分成多行。然而,服务将打印​​如果我剥离空格如下:

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解串器。有谁知道为什么会发生这种情况,我能做些什么呢?

回答

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); 
    } 
} 
+0

谢谢。有没有来自Microsoft的知识库文章,这是一个错误? – Imran 2016-03-24 20:07:31

3

不幸的是,我对此没有很好的答案。我们遇到了同样的问题,并与Microsoft联系。他们坚持认为,由于考虑到性能问题,他们所做的事情是可以的,考虑到整个管道的工作原理,这是可笑的。

所以我们的经验是,许多地方的空白造成了问题。此外,如果您将__type字段放在第一个字段的任何位置,您将获得超类型。

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

编辑:正如carlosfigueira指出的,这显然已被固定在4.5。我还没有尝试过。

+1

作为一种替代方案,我认为唯一明智的做法是直接将JSON传递给您的服务,然后使用类似[Json.Net](http://james.newtonking.com/pages /json-net.aspx)手动解串对象。无论是或者试图找出一个预处理器,拦截所有JSON并在消息到达解串器之前去除任何空白/换行符,这可能会导致错误进一步下降。 – 2011-12-29 00:26:18