Json.net serializza / deserializza i tipi derivati?


99

json.net (newtonsoft)
Sto esaminando la documentazione ma non riesco a trovare nulla su questo o il modo migliore per farlo.

public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

JsonConvert.Deserialize<List<Base>>(text);

Ora ho oggetti derivati ​​nell'elenco serializzato. Come deserializzare l'elenco e recuperare i tipi derivati?


Non è così che funziona l'eredità. È possibile specificare JsonConvert.Deserialize <Derived> (testo); per includere il campo Nome. Poiché Derived è una base (non il contrario), Base non sa nulla della definizione di Derived.
M.Babcock

Scusa, chiarito un po '. Il problema è che ho un elenco che contiene oggetti di base e derivati. Quindi ho bisogno di capire come dire a newtonsoft come deserializzare gli elementi derivati.
Il

L'ho risolto. Ho lo stesso problema
Luis Carlos Chavarría

Risposte:


46

Se stai memorizzando il tipo nel tuo text(come dovresti essere in questo scenario), puoi usare il JsonSerializerSettings.

Vedi: come deserializzare JSON in IEnumerable <BaseType> con Newtonsoft JSON.NET

Stai attento, però. Usare qualcosa di diverso TypeNameHandling = TypeNameHandling.Nonepotrebbe esporti a una vulnerabilità di sicurezza .


24
Puoi anche usare TypeNameHandling = TypeNameHandling.Auto- questo aggiungerà una $typeproprietà SOLO per le istanze in cui il tipo dichiarato (cioè Base) non corrisponde al tipo di istanza (cioè Derived). In questo modo, non gonfia il tuo JSON tanto quanto TypeNameHandling.All.
AJ Richardson

Continuo a ricevere l'errore di risoluzione del tipo specificato in JSON "..., ...". Percorso "$ tipo", riga 1, posizione 82. Qualche idea?
briba

3
Fai attenzione quando lo utilizzi su un endpoint pubblico poiché apre problemi di sicurezza: alphabot.com/security/blog/2017/net/…
gjvdkamp

1
@gjvdkamp JEEZ grazie per questo, non lo sapevo. Aggiungerò al mio post.
kamranicus

96

Devi abilitare la gestione del nome del tipo e passarlo al (de) serializzatore come parametro delle impostazioni.

Base object1 = new Base() { Name = "Object1" };
Derived object2 = new Derived() { Something = "Some other thing" };
List<Base> inheritanceList = new List<Base>() { object1, object2 };

JsonSerializerSettings settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
string Serialized = JsonConvert.SerializeObject(inheritanceList, settings);
List<Base> deserializedList = JsonConvert.DeserializeObject<List<Base>>(Serialized, settings);

Ciò comporterà la deserializzazione corretta delle classi derivate. Uno svantaggio è che nominerà tutti gli oggetti che stai utilizzando, in quanto tale nominerà l'elenco in cui stai inserendo gli oggetti.


31
+1. Ho cercato su Google per 30 minuti fino a quando ho scoperto che è necessario utilizzare le stesse impostazioni per SerializeObject e DeserializeObject. Ho pensato che avrebbe usato $ type implicitamente se fosse presente durante la deserializzazione, sciocco io.
Erti-Chris Eelmaa

24
TypeNameHandling.Autolo farà anche tu, ed è più carino perché non scrive il nome del tipo di istanza quando corrisponde al tipo di campo / proprietà, come spesso accade per la maggior parte dei campi / proprietà.
Roman Starkov,

2
Questo non funziona quando la deserializzazione viene eseguita su un'altra soluzione / progetto. Durante la serializzazione il nome della soluzione è incorporato nel tipo: "SOLUTIONNAME.Models.Model". Alla deserializzazione sull'altra soluzione verrà generato "JsonSerializationException: Impossibile caricare l'assembly" SOLUTIONNAME ".
Sad CRUD Developer

19

Poiché la domanda è così popolare, potrebbe essere utile aggiungere cosa fare se si desidera controllare il nome della proprietà del tipo e il suo valore.

Il modo più lungo è scrivere messaggi personalizzati JsonConverterper gestire la (de) serializzazione controllando e impostando manualmente la proprietà del tipo.

Un modo più semplice è usare JsonSubTypes , che gestisce tutto il boilerplate tramite attributi:

[JsonConverter(typeof(JsonSubtypes), "Sound")]
[JsonSubtypes.KnownSubType(typeof(Dog), "Bark")]
[JsonSubtypes.KnownSubType(typeof(Cat), "Meow")]
public class Animal
{
    public virtual string Sound { get; }
    public string Color { get; set; }
}

public class Dog : Animal
{
    public override string Sound { get; } = "Bark";
    public string Breed { get; set; }
}

public class Cat : Animal
{
    public override string Sound { get; } = "Meow";
    public bool Declawed { get; set; }
}

4
Ho la necessità, ma non sono un fan di dover rendere la classe base consapevole di tutti i "KnownSubType" s ...
Matt Knowles

2
Ci sono altre opzioni se guardi la documentazione. Ho fornito solo l'esempio che mi piace di più.
rzippo

1
Questo è l'approccio più sicuro che non espone il servizio a caricare tipi arbitrari durante la deserializzazione.
David Burg

3

Usa questo JsonKnownTypes , è un modo molto simile da usare, basta aggiungere il discriminatore a json:

[JsonConverter(typeof(JsonKnownTypeConverter<BaseClass>))]
[JsonKnownType(typeof(Base), "base")]
[JsonKnownType(typeof(Derived), "derived")]
public class Base
{
    public string Name;
}
public class Derived : Base
{
    public string Something;
}

Ora, quando si serializza oggetto in JSON sarà aggiunge "$type"con "base"e "derived"il valore e sarà utilizzato per deserializzare

Esempio di elenco serializzato:

[
    {"Name":"some name", "$type":"base"},
    {"Name":"some name", "Something":"something", "$type":"derived"}
]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.