deserializzare la stringa che è stata serializzata con TypeNameHandling.All

Una class è stata serializzata utilizzando il seguente esempio

using Newtonsoft.Json; using System; namespace ConsoleAppCompare { class Program { static void Main(string[] args) { Movie movie = new Movie() { Name = "Avengers", Language = "En", Actors = new Character[] { new Character(){Name="Phil Coulson"},new Character(){Name="Tony Stark"} }}; Console.WriteLine(JsonConvert.SerializeObject(movie, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All })); Console.ReadLine(); } } class Movie { public string Name { get; set; } public string Language { get; set; } public Character[] Actors { get; set; } } class Character { public string Name { get; set; } } } 

L’esempio sopra riportato produce il seguente json

 { "$type": "ConsoleAppCompare.Movie, ConsoleAppCompare", "Name": "Avengers", "Language": "En", "Actors": { "$type": "ConsoleAppCompare.Character[], ConsoleAppCompare", "$values": [ { "$type": "ConsoleAppCompare.Character, ConsoleAppCompare", "Name": "Phil Coulson" }, { "$type": "ConsoleAppCompare.Character, ConsoleAppCompare", "Name": "Tony Stark" } ] } } 

Ora, su un altro programma , che non ha accesso ai modelli sopra,
Devo deserializzare la stringa su un object ma nulla di quello che ho provato sembra funzionare …

Per creare i miei nuovi modelli ho copiato il json nei miei appunti e ho usato la funzionalità “Incolla speciale” di Visual Studio

 using Newtonsoft.Json; using System; using System.IO; namespace ConsoleAppCompare { class Program { static void Main(string[] args) { var s = File.ReadAllText(@"C:\Users\nvovo\Desktop\asdf\aa.txt"); Rootobject movie = null; // nothing Works :( //movie =JsonConvert.DeserializeObject(s, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All }); //movie = JsonConvert.DeserializeObject(s, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None }); //movie = JsonConvert.DeserializeObject(s); Console.ReadLine(); } } public class Rootobject { public string type { get; set; } public string Name { get; set; } public string Language { get; set; } public Actors Actors { get; set; } } public class Actors { public string type { get; set; } public Values[] values { get; set; } } public class Values { public string type { get; set; } public string Name { get; set; } } } 

Posso fare qualcosa per questo o dovrei provare a trovare le classi originali?

Aggiornare

Non mi interessa la proprietà “$ tipo”. Non è nemmeno sui modelli originali. Voglio solo deserializzare il JSON in un modello fortemente tipizzato, incluse le collezioni (le mie classi reali hanno più livelli nidificati) ma i tipi generati automaticamente (usando Paste Json) non funzionano.

Se tutto quello che vuoi fare è ignorare le informazioni sul tipo, allora:

  • Se si deserializza con TypeNameHandling.None , le proprietà "$type" sugli oggetti vengono semplicemente ignorate e non causeranno problemi durante la deserializzazione.

  • Ma anche con TypeNameHandling.None , le proprietà "$type" per i valori di raccolta causano problemi perché il tipo di metadati generati per la raccolta contiene un ulteriore livello di nidificazione nel JSON:

    Con "type" :

     { "Actors": { "$type": "ConsoleAppCompare.Character[], ConsoleAppCompare", "$values": [] } } 

    E senza:

     { "Actors": [] } 

    Quando deserializza JSON con TypeNameHandling.None , se viene rilevata una raccolta serializzata con il livello di nidificazione aggiuntivo, viene generata un’eccezione.

    Quindi è necessario un modo per eliminare il livello extra di nidificazione durante la deserializzazione, ad esempio con un JsonConverter personalizzato . E in questa risposta alla domanda Strategie per la migrazione del documento serializzato Json.NET tra versioni / formati ce n’è una già scritta e disponibile per l’uso: IgnoreCollectionTypeConverter .

In questo modo puoi definire i tuoi modelli come segue:

 public class Rootobject { public string Name { get; set; } public string Language { get; set; } public List Actors { get; set; } } public class Actor { public string Name { get; set; } } 

E deserializzare come segue:

 var settings = new JsonSerializerSettings { Converters = { new IgnoreCollectionTypeConverter() }, }; var movie = JsonConvert.DeserializeObject(s, settings); 

Campione di violino

Gli appunti:

  • IgnoreCollectionTypeConverter è progettato per funzionare con raccolte di lettura / scrittura, motivo per cui ho modificato gli Actors da un array a un List .

  • Se è necessario elaborare le informazioni sul tipo piuttosto che ignorarlo, sarà necessario creare un ISerializationBinder personalizzato. Vedi Custom SerializationBinder per i dettagli. La domanda Come creare un SerializationBinder per il formattatore binario che gestisce lo spostamento di tipi da un assembly e uno spazio dei nomi a un altro offre una soluzione per la creazione di un raccoglitore di serializzazione personalizzato che gestisce l’annidamento di generici.

Aggiornare

Hai chiesto, voglio solo deserializzare il json in un modello fortemente tipizzato, comprese le collezioni (le mie classi reali hanno più livelli nidificati) ma i tipi generati automaticamente (usando Paste Json) non funzionano.

Durante il processo di sviluppo, è ansible utilizzare LINQ to JSON per caricare il JSON in memoria, rimuovere tutti i metadati "$type" e scrivere su una nuova stringa JSON. Quindi, puoi prendere quella stringa pulita e usarla per “Incolla Json come classi”.

Il seguente metodo di estensione farà il lavoro necessario:

 public static class JsonExtensions { const string valuesName = "$values"; const string typeName = "$type"; public static JToken RemoveTypeMetadata(this JToken root) { if (root == null) throw new ArgumentNullException(); var types = root.SelectTokens(".." + typeName).Select(v => (JProperty)v.Parent).ToList(); foreach (var typeProperty in types) { var parent = (JObject)typeProperty.Parent; typeProperty.Remove(); var valueProperty = parent.Property(valuesName); if (valueProperty != null && parent.Count == 1) { // Bubble the $values collection up removing the synthetic container object. var value = valueProperty.Value; if (parent == root) { root = value; } // Remove the $values property, detach the value, then replace it in the parent's parent. valueProperty.Remove(); valueProperty.Value = null; if (parent.Parent != null) { parent.Replace(value); } } } return root; } } 

Esempio di lavoro .Net fiddle che accetta la stringa JSON di input e restituisce:

 { "Name": "Avengers", "Language": "En", "Actors": [ { "Name": "Phil Coulson" }, { "Name": "Tony Stark" } ] }