使用JSON在WCF服务中保留多态类型 [英] Preserving Polymorphic Types in a WCF Service using JSON

查看:99
本文介绍了使用JSON在WCF服务中保留多态类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个使用webHttpBinding端点的C#WCF服务,该端点将接收和返回JSON格式的数据.发送/接收的数据需要使用多态类型,以便可以在同一数据包"中交换不同类型的数据.我有以下数据模型:

I have a C# WCF service using a webHttpBinding endpoint that will receive and return data in JSON format. The data to send/receive needs to use a polymorphic type so that data of different types can be exchanged in the same "data packet". I have the following data model:

[DataContract]
public class DataPacket
{
    [DataMember]
    public List<DataEvent> DataEvents { get; set; }
}

[DataContract]
[KnownType(typeof(IntEvent))]
[KnownType(typeof(BoolEvent))]
public class DataEvent
{
    [DataMember]
    public ulong Id { get; set; }

    [DataMember]
    public DateTime Timestamp { get; set; }

    public override string ToString()
    {
        return string.Format("DataEvent: {0}, {1}", Id, Timestamp);
    }
}

[DataContract]
public class IntEvent : DataEvent
{
    [DataMember]
    public int Value { get; set; }

    public override string ToString()
    {
        return string.Format("IntEvent: {0}, {1}, {2}", Id, Timestamp, Value);
    }
}

[DataContract]
public class BoolEvent : DataEvent
{
    [DataMember]
    public bool Value { get; set; }

    public override string ToString()
    {
        return string.Format("BoolEvent: {0}, {1}, {2}", Id, Timestamp, Value);
    }
}

我的服务将在单个数据包中发送/接收子类型事件(IntEvent,BoolEvent等),如下所示:

My service will send/receive the sub-type events (IntEvent, BoolEvent etc.) in a single data packet, as follows:

[ServiceContract]
public interface IDataService
{
    [OperationContract]
    [WebGet(UriTemplate = "GetExampleDataEvents")]
    DataPacket GetExampleDataEvents();

    [OperationContract]
    [WebInvoke(UriTemplate = "SubmitDataEvents", RequestFormat = WebMessageFormat.Json)]
    void SubmitDataEvents(DataPacket dataPacket);
}

public class DataService : IDataService
{
    public DataPacket GetExampleDataEvents()
    {
        return new DataPacket {
            DataEvents = new List<DataEvent>
            {
                new IntEvent  { Id = 12345, Timestamp = DateTime.Now, Value = 5 },
                new BoolEvent { Id = 45678, Timestamp = DateTime.Now, Value = true }
            }
        };
    }

    public void SubmitDataEvents(DataPacket dataPacket)
    {
        int i = dataPacket.DataEvents.Count; //dataPacket contains 2 events, but both are type DataEvent instead of IntEvent and BoolEvent
        IntEvent intEvent = dataPacket.DataEvents[0] as IntEvent;
        Console.WriteLine(intEvent.Value); //null pointer as intEvent is null since the cast failed
    }
}

但是,当我将数据包提交给SubmitDataEvents方法时,我得到了DataEvent类型,并试图将它们强制转换回其基本类型(仅出于测试目的),结果是InvalidCastException.我的包裹是:

When I submit my packet to the SubmitDataEvents method though, I get DataEvent types and trying to cast them back to their base types (just for testing purposes) results in an InvalidCastException. My packet is:

POST http://localhost:4965/DataService.svc/SubmitDataEvents HTTP/1.1
User-Agent: Fiddler
Host: localhost:4965
Content-Type: text/json
Content-Length: 340

{
    "DataEvents": [{
        "__type": "IntEvent:#WcfTest.Data",
        "Id": 12345,
        "Timestamp": "\/Date(1324905383689+0000)\/",
        "Value": 5
    }, {
        "__type": "BoolEvent:#WcfTest.Data",
        "Id": 45678,
        "Timestamp": "\/Date(1324905383689+0000)\/",
        "Value": true
    }]
}

很长的歉意,但是我能做些什么来保留每个对象的基本类型吗?我以为将类型提示添加到JSON中,并将KnownType属性添加到DataEvent会允许我保留类型-但这似乎行不通.

Apologies for the long post, but is there anything I can do to preserve the base types of each object? I thought adding the type hint to the JSON and the KnownType attributes to DataEvent would allow me to preserve the types - but it doesn't seem to work.

编辑:如果我以XML格式(使用Content-Type: text/xml而不是text/json)将请求发送到SubmitDataEvents,则List<DataEvent> DataEvents确实包含了子类型而不是超级类型. -类型.一旦我将请求设置为text/json并发送了上述数据包,我就只能得到超级类型,而不能将它们转换为子类型.我的XML请求正文是:

Edit: If I send the request to SubmitDataEvents in XML format (with Content-Type: text/xml instead of text/json) then the List<DataEvent> DataEvents does contain the sub-types instead of the super-type. As soon as I set the request to text/json and send the above packet then I only get the super-type and I can't cast them to the sub-type. My XML request body is:

<ArrayOfDataEvent xmlns="http://schemas.datacontract.org/2004/07/WcfTest.Data">
  <DataEvent i:type="IntEvent" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <Id>12345</Id>
    <Timestamp>1999-05-31T11:20:00</Timestamp>
    <Value>5</Value>
  </DataEvent>
  <DataEvent i:type="BoolEvent" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
    <Id>56789</Id>
    <Timestamp>1999-05-31T11:20:00</Timestamp>
    <Value>true</Value>
  </DataEvent>
</ArrayOfDataEvent>

编辑2 :在下面的Pavel评论后更新了服务说明.在Fiddler2中发送JSON数据包时,这仍然不起作用.我只是得到一个包含DataEvent而不是IntEventBoolEventList.

Edit 2: Updated service description after Pavel's comments below. This still doesn't work when sending the JSON packet in Fiddler2. I just get a List containing DataEvent instead of IntEvent and BoolEvent.

编辑3 :如Pavel所建议的,这是System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString()的输出.在我看来还可以.

Edit 3: As Pavel suggested, here is the output from System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString(). Looks OK to me.

<root type="object">
    <DataEvents type="array">
        <item type="object">
            <__type type="string">IntEvent:#WcfTest.Data</__type> 
            <Id type="number">12345</Id> 
            <Timestamp type="string">/Date(1324905383689+0000)/</Timestamp> 
            <Value type="number">5</Value> 
        </item>
        <item type="object">
            <__type type="string">BoolEvent:#WcfTest.Data</__type> 
            <Id type="number">45678</Id> 
            <Timestamp type="string">/Date(1324905383689+0000)/</Timestamp> 
            <Value type="boolean">true</Value> 
        </item>
    </DataEvents>
</root>

在跟踪数据包的反序列化时,我在跟踪中得到以下消息:

When tracing the deserialization of the packet, I get the following messages in the trace:

<TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Verbose">
    <TraceIdentifier>http://msdn.microsoft.com/en-GB/library/System.Runtime.Serialization.ElementIgnored.aspx</TraceIdentifier>
    <Description>An unrecognized element was encountered in the XML during deserialization which was ignored.</Description>
    <AppDomain>1c7ccc3b-4-129695001952729398</AppDomain>
    <ExtendedData xmlns="http://schemas.microsoft.com/2006/08/ServiceModel/StringTraceRecord">
        <Element>:__type</Element>
    </ExtendedData>
</TraceRecord>

此消息重复4次(使用__type作为元素两次,使用Value两次).看起来像类型提示信息被忽略了,然后Value元素被忽略了,因为数据包被反序列化为DataEvent而不是IntEvent/BoolEvent.

This message is repeated 4 times (twice with __type as the element and twice with Value). Looks like the type hinting information is being ignored then the Value elements are ignored as the packet is deserialized to DataEvent instead of IntEvent/BoolEvent.

推荐答案

感谢Pavel Gatilov,我现在找到了解决此问题的方法.对于将来可能会被此问题困扰的任何人,我将在此处将其添加为单独的答案.

Thanks to Pavel Gatilov, I've now found the solution to this problem. I'll add it as separate answer here for anyone who may be caught out by this in future.

问题在于JSON解串器似乎不太接受空格.我正在发送的数据包中的数据是漂亮打印的",带有换行符和空格,以使其更具可读性.但是,当对该数据包进行反序列化时,这意味着在寻找"__type"提示时,JSON反序列化程序正在查看该数据包的错误部分.这意味着错过了类型提示,并且将数据包反序列化为错误的类型.

The problem is that the JSON deserializer doesn't seem to be very accepting of whitespace. The data in the packet that I was sending was "pretty printed" with line breaks and spaces to make it more readable. However, when this packet was deserialized, this meant that when looking for the "__type" hint, the JSON deserializer was looking at the wrong part of the packet. This meant that the type hint was missed and the packet was deserialized as the wrong type.

以下数据包正常工作:

POST http://localhost:6463/DataService.svc/SubmitDataEvents HTTP/1.1
User-Agent: Fiddler
Content-Type: text/json
Host: localhost:6463
Content-Length: 233

{"DataEvents":[{"__type":"IntEvent:#WebApplication1","Id":12345,"Timestamp":"\/Date(1324905383689+0000)\/","IntValue":5},{"__type":"BoolEvent:#WebApplication1","Id":45678,"Timestamp":"\/Date(1324905383689+0000)\/","BoolValue":true}]}

但是,此数据包不起作用:

However, this packet doesn't work:

POST http://localhost:6463/DataService.svc/SubmitDataEvents HTTP/1.1
User-Agent: Fiddler
Content-Type: text/json
Host: localhost:6463
Content-Length: 343

{
    "DataEvents": [{
        "__type": "IntEvent:#WebApplication1",
        "Id": 12345,
        "Timestamp": "\/Date(1324905383689+0000)\/",
        "IntValue": 5
    }, {
        "__type": "BoolEvent:#WebApplication1",
        "Id": 45678,
        "Timestamp": "\/Date(1324905383689+0000)\/",
        "BoolValue": true
    }]
}

这些包与换行符和空格完全相同.

These packets are exactly the same apart from the line breaks and spaces.

这篇关于使用JSON在WCF服务中保留多态类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆