2016-08-09 19 views
6

Po ustawieniu Json.NET do serializacji z ustawieniem TypeNameHandling na TypeNameHandling.Auto, poprawnie ustawia typ $ dla właściwości podrzędnych obiektu, ale nie robi tego dla obiektu głównego, który jest serializowany. Czemu?

Proszę rozważyć następujące repro:

public class Animal 
{ 
    public Animal[] Offspring { get; set; } 
} 

public class Dog : Animal {} 

Animal fido = new Dog 
{ 
    Offspring = new Animal[] { new Dog() } 
}; 

var json = JsonConvert.SerializeObject(fido, 
    new JsonSerializerSettings 
    { 
     TypeNameHandling = TypeNameHandling.Auto 
    }); 

JSON emitowane do zmiennej json jest:

{ 
    "Offspring": [{ 
     "$type": "MyApp.Dog, MyApp", 
     "Offspring": null 
    }] 
} 

Json.NET Documentation mówi, że dla TypeNameHandling.Auto zachowanie jest:

Podaj nazwę typu .NET, gdy typ obiektu jest t jest serializowany nie jest tym samym, co jego zadeklarowany typ.

Moje pytanie brzmi - dlaczego fido nie ma "$type": "MyApp.Dog, MyApp", jak jego szczenię? :)


UPDATE: Znalazłem się z przyjętą odpowiedzi this question że mogę zmusić $ typ, który zostanie dodany w ten sposób:

var json = JsonConvert.SerializeObject(fido, 
    typeof(Animal), 
    new JsonSerializerSettings 
    { 
     TypeNameHandling = TypeNameHandling.Auto, 
     Formatting = Formatting.Indented 
    }); 

Ale moje pytanie nadal trzyma - Dlaczego Json.NET nie robi tego samodzielnie, jak w dokumentacji?

Odpowiedz

4

Krótka odpowiedź: nie dlatego, że nie może.

Jak stwierdzono w pytaniu, ustawienie TypeNameHandling na Auto powoduje, że Json.Net dołącza nazwę typu .NET, gdy rzeczywisty (działający) typ serializowanego obiektu nie jest taki sam jak zadeklarowany (kompilacja). czas). Aby to zrobić, Json.Net musi znać oba typy dla każdego obiektu.

Dla wszystkiego wewnątrz obiektu głównego jest to proste: wystarczy pobrać typ obiektu głównego za pomocą GetType(), a następnie użyć odbicia, aby uzyskać wszystkie zadeklarowane właściwości i ich typy, a dla każdego porównać zadeklarowany typ do rzeczywisty typ, aby zobaczyć, czy się różnią. Jeśli tak, wypisz nazwę typu.

Ale dla samego obiektu głównego Json.Net nie ma dostępu do obu typów. Wszystkie informacje, które posiada, to obiekt, do którego odwołuje się fido, którego typem środowiska wykonawczego jest Dog. Json.Net nie ma możliwości, aby odkryć, że zmienna fido została zadeklarowana jako Animal, chyba że podasz ten kontekst w jakiś sposób. I właśnie dlatego Json.Net zapewnia przeciążenia w postaci SerializeObject, które umożliwiają określenie typu kompilacji obiektu, który jest serializowany. Musisz musi użyć jednego z tych przeciążeń, jeśli chcesz, aby ustawienie TypeNameHandling.Auto zadziałało dla obiektu głównego.

1

Brian jest absolutnie poprawny, Json.NET nie ma możliwości poznania zadeklarowanego czasu kompilacji obiektu, który jest przekazywany, ponieważ parametr value jest zadeklarowany jako object. Łatwo to naprawić, jeśli Json.NET dodał generyczne metody serializacji, aby typ deklarowany podczas kompilacji automatycznie przepływał do Json.NET, ale autor biblioteki podjął decyzję przeciw mojej propozycji dla tego here.

Jako alternatywę, zawarłem wszystkie moje potrzeby serializacji json (de) w klasie JsonHelper za pomocą ogólnych metod szeregowania, które używają wyrażenia typeof do automatycznego przekazywania zadeklarowanej w czasie kompilacji wartości wartości, która ma zostać przekształcona do postaci szeregowej.