Mam usługę C# WCF, używając punktu końcowego webHttpBinding, który będzie odbierać i zwracać dane w formacie JSON. Dane wysyłane/odbierane muszą być typu polimorficznego, aby dane różnych typów mogły być wymieniane w tym samym "pakiecie danych". Mam następujący model danych:Zachowanie polimorficznych typów w usłudze WCF przy użyciu 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);
}
}
Moja usługa będzie wysyłać/odbierać zdarzenia podtyp (IntEvent, BoolEvent etc.) w jednym pakiecie danych, jak następuje:
[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
}
}
Kiedy Prześlij mój pakiet do metody SubmitDataEvents
, jednak otrzymuję typy DataEvent
i próbuję odesłać je z powrotem do ich typów bazowych (tylko w celach testowych) w wyniku InvalidCastException
. Moja paczka jest:
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
}]
}
Przepraszam za długi post, ale jest coś, co można zrobić, aby zachować typy bazowe każdego obiektu? Pomyślałem, że dodanie wskazówki typu do JSON i atrybutów KnownType do DataEvent
pozwoliłoby mi zachować typy - ale wydaje się, że nie działa.
Edit: Jeśli mogę wysłać wniosek do SubmitDataEvents
w formacie XML (z Content-Type: text/xml
zamiast text/json
) wtedy List<DataEvent> DataEvents
nie zawierają podtypy zamiast super-type. Jak tylko ustawię żądanie na text/json
i wyślę powyższy pakiet, otrzymam tylko supertyp i nie mogę ich przesłać do podtypu. Moja prośba ciało XML jest:
<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>
Edycja 2: Poprawiono opis usługi po komentarzach Pavel jest poniżej. To nadal nie działa podczas wysyłania pakietu JSON w Fiddler2. Właśnie dostaję List
zawierający DataEvent
zamiast IntEvent
i BoolEvent
.
Edytuj 3: Jak sugerował Pavel, tutaj jest wyjście z System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString()
. Wygląda mi dobrze.
<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>
Podczas śledzenia deserializacji opakowania, pojawiają się następujące komunikaty w śladowych:
<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>
Komunikat ten powtarza się 4-krotnie (dwukrotnie __type
jako element i dwukrotnie Value
). Wygląda na to, że informacje o podpowiedzi typu są ignorowane, a elementy Value
są ignorowane, ponieważ pakiet jest przekształcany do postaci szeregowej na DataEvent
zamiast IntEvent
/BoolEvent
.
Wow ... Cieszę się, że Twój problem został rozwiązany.Jednak sformatowany JSON działał dobrze dla mnie, zawsze kopiowałem-wklejałem do Fiddlera i nie miałem problemów. Sądzę, że albo nie zainstalowałeś wszystkich aktualizacji dla platformy .NET, albo najnowsza wersja ma dziwny błąd związany z przetwarzaniem znaków białych znaków. Jestem całkowicie zakłopotany. –
Nie wstydź się, nigdy bym tego nie znalazł bez twojej pomocy. Jedyny sposób, jaki odkryłem, polegał na debugowaniu kodu źródłowego WCF, który napisałeś powyżej. Zauważyłem, że wartość 'offset' była zawsze zbyt niska i zauważyłem, że włączenie znaków' \ r' i '\ n' w buforze komunikatów w jakiś sposób wyrzuciło obliczenia przesunięcia. Mogę zadać kolejne pytanie, aby sprawdzić, czy ktoś wie, dlaczego białe spacje zakłócają parsowanie, ponieważ chciałbym, aby elastyczność została uwzględniona lub nie. –
Och, to powinno być raczej "zdezorientowane" niż "zawstydzone" :). Przy okazji, znalazłem opis podobnego problemu [w tym artykule] (http://blog.js-development.com/2010/05/n-will-break-your-json-jquery-wcf.html). –