Deserializzazione JSON per astrarre class

Sto provando a deserializzare una stringa JSON in una class concreta, che eredita da una class astratta, ma non riesco a farla funzionare. Ho cercato su Google e provato alcune soluzioni, ma non sembrano funzionare neanche.

Questo è quello che ho adesso:

abstract class AbstractClass { } class ConcreteClass { } public AbstractClass Decode(string jsonString) { JsonSerializerSettings jss = new JsonSerializerSettings(); jss.TypeNameHandling = TypeNameHandling.All; return (AbstractClass)JsonConvert.DeserializeObject(jsonString, null, jss); } 

Tuttavia, se provo a lanciare l’object risultante, semplicemente non funziona.

Il motivo per cui non utilizzo DeserializeObject è che ho molte lezioni concrete .

Eventuali suggerimenti?

  • Sto usando Newtonsoft.Json

provare qualcosa di simile

 public AbstractClass Decode(string jsonString) { var jss = new JavaScriptSerializer(); return jss.Deserialize(jsonString); } 

AGGIORNARE
per questo scenario, tutto funziona come vuoi tu

 public abstract class Base { public abstract int GetInt(); } public class Der:Base { int g = 5; public override int GetInt() { return g+2; } } public class Der2 : Base { int i = 10; public override int GetInt() { return i+17; } } .... var jset = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; Base b = new Der() string json = JsonConvert.SerializeObject(b, jset); .... Base c = (Base)JsonConvert.DeserializeObject(json, jset); 

dove c type è test.Base {test.Der}

AGGIORNARE

@Gusman suggerisce di usare TypeNameHandling.Objects invece di TypeNameHandling.All . È sufficiente e produrrà una serializzazione meno dettagliata.

Si potrebbe non voler usare TypeNameHandling (perché si vuole un json più compatto o si vuole usare un nome specifico per la variabile type diversa da “$ type”). Nel frattempo, l’ approccio customerCreationConverter non funzionerà se si vuole deserializzare la class base in una qualsiasi delle classi derivate multiple senza sapere quale usare in anticipo.

Un’alternativa è usare un tipo int o altro nella class base e definire un JsonConverter.

 [JsonConverter(typeof(BaseConverter))] abstract class Base { public int ObjType { get; set; } public int Id { get; set; } } class DerivedType1 : Base { public string Foo { get; set; } } class DerivedType2 : Base { public string Bar { get; set; } } 

Il JsonConverter per la class base può quindi deserializzare l’object in base al suo tipo. La complicazione è che per evitare uno stack overflow (dove JsonConverter si chiama ripetutamente), durante questa deserializzazione deve essere usato un resolver di contratto personalizzato.

 public class BaseSpecifiedConcreteClassConverter : DefaultContractResolver { protected override JsonConverter ResolveContractConverter(Type objectType) { if (typeof(Base).IsAssignableFrom(objectType) && !objectType.IsAbstract) return null; // pretend TableSortRuleConvert is not specified (thus avoiding a stack overflow) return base.ResolveContractConverter(objectType); } } public class BaseConverter : JsonConverter { static JsonSerializerSettings SpecifiedSubclassConversion = new JsonSerializerSettings() { ContractResolver = new BaseSpecifiedConcreteClassConverter() }; public override bool CanConvert(Type objectType) { return (objectType == typeof(Base)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { JObject jo = JObject.Load(reader); switch (jo["ObjType"].Value()) { case 1: return JsonConvert.DeserializeObject(jo.ToString(), SpecifiedSubclassConversion); case 2: return JsonConvert.DeserializeObject(jo.ToString(), SpecifiedSubclassConversion); default: throw new Exception(); } throw new NotImplementedException(); } public override bool CanWrite { get { return false; } } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); // won't be called because CanWrite returns false } } 

Questo è tutto. Ora puoi usare serializzare / deserializzare qualsiasi class derivata. Puoi anche utilizzare la class base in altre classi e serializzare / deserializzare quelli senza alcun lavoro aggiuntivo:

 class Holder { public List Objects { get; set; } } string json = @" [ { ""Objects"" : [ { ""ObjType"": 1, ""Id"" : 1, ""Foo"" : ""One"" }, { ""ObjType"": 1, ""Id"" : 2, ""Foo"" : ""Two"" }, ] }, { ""Objects"" : [ { ""ObjType"": 2, ""Id"" : 3, ""Bar"" : ""Three"" }, { ""ObjType"": 2, ""Id"" : 4, ""Bar"" : ""Four"" }, ] }, ]"; List list = JsonConvert.DeserializeObject>(json); string serializedAgain = JsonConvert.SerializeObject(list); Debug.WriteLine(serializedAgain); 

Vorrei suggerire di utilizzare CustomCreationConverter nel modo seguente:

 public enum ClassDiscriminatorEnum { ChildClass1, ChildClass2 } public abstract class BaseClass { public abstract ClassDiscriminatorEnum Type { get; } } public class Child1 : BaseClass { public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass1; public int ExtraProperty1 { get; set; } } public class Child2 : BaseClass { public override ClassDiscriminatorEnum Type => ClassDiscriminatorEnum.ChildClass2; } public class BaseClassConverter : CustomCreationConverter { private ClassDiscriminatorEnum _currentObjectType; public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jobj = JObject.ReadFrom(reader); _currentObjectType = jobj["Type"].ToObject(); return base.ReadJson(jobj.CreateReader(), objectType, existingValue, serializer); } public override BaseClass Create(Type objectType) { switch (_currentObjectType) { case ClassDiscriminatorEnum.ChildClass1: return new Child1(); case ClassDiscriminatorEnum.ChildClass2: return new Child2(); default: throw new NotImplementedException(); } } } 
  public class CustomConverter : JsonConverter { private static readonly JsonSerializer Serializer = new JsonSerializer(); public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jObject = JObject.Load(reader); var typeString = jObject.Value("Kind"); //Kind is a property in json , from which we know type of child classs var requiredType = RecoverType(typeString); return Serializer.Deserialize(jObject.CreateReader(), requiredType); } private Type RecoverType(string typeString) { if (typeString.Equals(type of child class1, StringComparison.OrdinalIgnoreCase)) return typeof(childclass1); if (typeString.Equals(type of child class2, StringComparison.OrdinalIgnoreCase)) return typeof(childclass2); throw new ArgumentException("Unrecognized type"); } public override bool CanConvert(Type objectType) { return typeof(Base class).IsAssignableFrom(objectType) || typeof((Base class) == objectType; } public override bool CanWrite { get { return false; } } } 

Ora aggiungi questo convertitore in JsonSerializerSettings come sotto

  var jsonSerializerSettings = new JsonSerializerSettings(); jsonSerializerSettings.Converters.Add(new Newtonsoft.Json.Converters.StringEnumConverter()); jsonSerializerSettings.Converters.Add(new CustomConverter()); 

Dopo aver aggiunto serializzare o deserializzare l’object della class base come di seguito

  JsonConvert.DeserializeObject("json string", jsonSerializerSettings );