Próbuję rozszerzyć przykład JSON.net podany tutaj http://james.newtonking.com/projects/json/help/CustomCreationConverter.html
Mam inną podklasę wywodzącą się z klasy bazowej / interfejsu
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
List<Person> people = new List<Person>
{
new Employee(),
new Employee(),
new Artist(),
};
Jak deserializować obserwowanie Jsona z powrotem do Listy <Osoba>
[
{
"Department": "Department1",
"JobTitle": "JobTitle1",
"FirstName": "FirstName1",
"LastName": "LastName1"
},
{
"Department": "Department2",
"JobTitle": "JobTitle2",
"FirstName": "FirstName2",
"LastName": "LastName2"
},
{
"Skill": "Painter",
"FirstName": "FirstName3",
"LastName": "LastName3"
}
]
Nie chcę używać TypeNameHandling JsonSerializerSettings. W szczególności szukam niestandardowej implementacji JsonConverter do obsługi tego. Dokumentacja i przykłady wokół tego są dość rzadkie w sieci. Nie mogę poprawnie przesłonić implementacji metody ReadJson () w JsonConverter.
c#
json
json.net
deserialization
Snakebyte
źródło
źródło
Odpowiedzi:
Korzystając ze standardu
CustomCreationConverter
, miałem problemy z wygenerowaniem poprawnego typu (Person
lubEmployee
), ponieważ aby to ustalić, musisz przeanalizować JSON i nie ma wbudowanej możliwości, aby to zrobić przy użyciu tejCreate
metody.Znalazłem wątek dyskusyjny dotyczący konwersji typów i okazało się, że udziela odpowiedzi. Oto link: Konwersja typów .
Wymagana jest podklasa
JsonConverter
, przesłanianieReadJson
metody i tworzenie nowejCreate
metody abstrakcyjnej , która akceptuje aJObject
.Zastąpiona
ReadJson
metoda tworzy aJObject
i wywołujeCreate
metodę (zaimplementowaną przez naszą pochodną klasę konwertera), przekazującJObject
instancję.To
JObject
wystąpienie można następnie przeanalizować w celu ustalenia poprawnego typu, sprawdzając istnienie niektórych pól.Przykład
źródło
JsonReader
tworzone wReadJson
sposób nie dziedziczy każdy z oryginalnej wartości konfiguracyjnych czytelnika (Culture
,DateParseHandling
,DateTimeZoneHandling
,FloatParseHandling
, etc ...). Te wartości należy skopiować przed użyciem nowegoJsonReader
wserializer.Populate()
.JsonConverter
ma właściwość o nazwieCanRead
iCanWrite
. Jeśli nie potrzebujesz niestandardowejWriteJson
implementacji, wystarczy pozwolić naCanWrite
powrótFALSE
. System powróci do domyślnego zachowania. @jdavies: Dodaj to do swojej odpowiedzi. W przeciwnym razie nastąpi awaria podczas serializacji.Powyższe rozwiązanie
JsonCreationConverter<T>
jest dostępne w Internecie, ale ma wadę, która objawia się w rzadkich przypadkach. Nowy JsonReader utworzony w metodzie ReadJson nie dziedziczy żadnych oryginalnych wartości konfiguracyjnych czytnika (Culture, DateParseHandling, DateTimeZoneHandling, FloatParseHandling itp.). Te wartości należy skopiować przed użyciem nowego JsonReader w serializer.Populate ().To najlepsze, co mogłem wymyślić, aby rozwiązać niektóre problemy z powyższą implementacją, ale nadal uważam, że pewne rzeczy są pomijane:
Aktualizacja Zaktualizowałem to, aby uzyskać bardziej jednoznaczną metodę, która tworzy kopię istniejącego czytnika. To tylko podsumowuje proces kopiowania poszczególnych ustawień JsonReader. Idealnie ta funkcja byłaby utrzymywana w samej bibliotece Newtonsoft, ale na razie możesz użyć następujących opcji:
Należy tego użyć w następujący sposób:
Starsze rozwiązanie wygląda następująco:
źródło
Pomyślałem, że podzielę się również rozwiązaniem opartym na tym, które działa z atrybutem Knowntype za pomocą odbicia, musiałem uzyskać klasę pochodną z dowolnej klasy podstawowej, rozwiązanie może skorzystać z rekurencji, aby znaleźć najlepiej pasującą klasę, chociaż nie potrzebowałem jej w mojej W takim przypadku dopasowanie jest wykonywane przez typ podany konwerterowi, jeśli ma znane typy, skanuje je wszystkie, dopóki nie dopasuje typu, który ma wszystkie właściwości wewnątrz ciągu JSON, zostanie wybrana pierwsza pasująca.
użycie jest tak proste jak:
w powyższym przypadku ret będzie typu B.
Klasy JSON:
Kod konwertera:
źródło
Projekt JsonSubTypes implementuje ogólny konwerter, który obsługuje tę funkcję za pomocą atrybutów.
W przypadku konkretnej próbki przedstawionej tutaj, jak to działa:
źródło
Jest to rozszerzenie odpowiedzi totemu. Robi to w zasadzie to samo, ale dopasowanie właściwości jest oparte na serializowanym obiekcie json, a nie odzwierciedla obiektu .net. Jest to ważne, jeśli używasz [JsonProperty], używasz CamelCasePropertyNamesContractResolver lub robisz cokolwiek innego, co spowoduje, że Json nie będzie pasował do obiektu .net.
Użycie jest proste:
Kod konwertera:
źródło
Jako kolejna odmiana znanego rozwiązania typu Totem, możesz użyć odbicia, aby utworzyć ogólny resolver typu, aby uniknąć potrzeby używania znanych atrybutów typu.
Wykorzystuje technikę podobną do GenericResolver firmy Juval Lowy dla WCF.
Tak długo, jak twoja klasa podstawowa jest abstrakcyjna lub jest interfejsem, znane typy będą określane automatycznie, bez konieczności dekorowania znanymi atrybutami typów.
W moim własnym przypadku zdecydowałem się użyć właściwości $ type do oznaczenia typu w moim obiekcie json, zamiast próbować określić go na podstawie właściwości, chociaż możesz pożyczyć od innych rozwiązań tutaj, aby użyć określania opartego na właściwości.
Następnie można go zainstalować jako formatyzator
źródło
Oto inne rozwiązanie, które pozwala uniknąć użycia
jObject.CreateReader()
, a zamiast tego tworzy noweJsonTextReader
(takie zachowanie jest używane przezJsonCreate.Deserialze
metodę domyślną :źródło
Często implementacja będzie istnieć w tej samej przestrzeni nazw co interfejs. Więc wpadłem na to:
Dlatego możesz dołączyć to globalnie tak:
źródło
Korzystając z idei totemu i zlangnera , stworzyłem taki,
KnownTypeConverter
który będzie w stanie określić najbardziej odpowiedni spadkobierca, biorąc pod uwagę, że dane json mogą nie zawierać elementów opcjonalnych.Tak więc usługa wysyła odpowiedź JSON, która zawiera tablicę dokumentów (przychodzących i wychodzących). Dokumenty mają zarówno wspólny zestaw elementów, jak i różne. W takim przypadku elementy związane z dokumentami wychodzącymi są opcjonalne i mogą być nieobecne.
W związku z tym utworzono klasę podstawową,
Document
która zawiera wspólny zestaw właściwości. Tworzone są również dwie klasy dziedziczące: -OutgoingDocument
dodaje dwa opcjonalne elementy"device_id"
i"msg_id"
; -IncomingDocument
dodaje jeden obowiązkowy element"sender_id"
;Zadanie polegało na stworzeniu konwertera, który w oparciu o dane i informacje JSON z FamousTypeAttribute będzie w stanie określić najodpowiedniejszą klasę, która pozwoli na zapisanie największej ilości otrzymanych informacji. Należy również wziąć pod uwagę, że dane Json mogą nie zawierać elementów opcjonalnych. Aby zmniejszyć liczbę porównań elementów json i właściwości modeli danych, postanowiłem nie brać pod uwagę właściwości klasy bazowej i korelować z elementami json jedynie właściwości klas dziedziczących.
Dane z usługi:
Modele danych:
Przetwornik:
PS: W moim przypadku, jeśli żaden konwerter nie został wybrany przez konwerter (może się tak zdarzyć, jeśli dane JSON zawierają informacje tylko z klasy podstawowej lub dane JSON nie zawierają elementów opcjonalnych z
OutgoingDocument
), to obiektOutgoingDocument
klasy zostanie utworzony, ponieważ jest wymieniony jako pierwszy na liścieKnownTypeAttribute
atrybutów. Na Twoje życzenie możesz zmienić implementacjęKnownTypeConverter
w tej sytuacji.źródło