使用JSON在WCF服务中保留多态类型 [英] Preserving Polymorphic Types in a WCF Service using JSON
问题描述
我有一个使用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
而不是IntEvent
和BoolEvent
的List
.
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屋!