Сохранение полиморфных типов в службе WCF с использованием JSON

У меня есть служба 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 DataEvents действительно содержит подтипы вместо супертипа.Как только я устанавливаю запрос на 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 ].

11
задан Adam Rodger 27 December 2011 в 23:20
поделиться