У меня есть служба WCF C #, использующая конечную точку webHttpBinding, которая будет получать и возвращать данные в формате JSON. Данные для отправки / получения должны использовать полиморфный тип, чтобы можно было обмениваться данными разных типов в одном «пакете данных». У меня следующая модель данных:
[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 и т. Д.) В одном пакете данных, как показано ниже:
[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
. Мой пакет:
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
позволит мне сохранить типы, но, похоже, это не работает.
Изменить : если я отправлю запрос на SubmitDataEvents
в формате XML (с Content-Type: text / xml
вместо text / json
), то список List
действительно содержит подтипы вместо супертипа.Как только я устанавливаю запрос на text / json
и отправляю вышеуказанный пакет, я получаю только супертип и не могу преобразовать их в подтип. Мой текст XML-запроса:
<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>
Edit 2 : Обновлено описание службы после комментариев Павла ниже. Это по-прежнему не работает при отправке пакета JSON в Fiddler2. Я просто получаю список List
, содержащий DataEvent
вместо IntEvent
и BoolEvent
.
Редактировать 3 : Как предложил Павел, вот результат работы System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString ()
. Мне кажется, все в порядке.
<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>
При отслеживании десериализации пакета я получаю следующие сообщения в трассировке:
<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
].