Ich möchte diesen Code über json.net serialisieren:
public interface ITestInterface
{
string Guid {get;set;}
}
public class TestClassThatImplementsTestInterface1
{
public string Guid { get;set; }
}
public class TestClassThatImplementsTestInterface2
{
public string Guid { get;set; }
}
public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
this.CollectionToSerialize.add( new TestClassThatImplementsTestInterface2() );
}
List<ITestInterface> CollectionToSerialize { get;set; }
}
Ich möchte ClassToSerializeViaJson mit json.net serialisieren / deserialisieren. Die Serialisierung funktioniert, aber die Deserialisierung gibt mir den folgenden Fehler:
Newtonsoft.Json.JsonSerializationException: Es konnte keine Instanz vom Typ ITestInterface erstellt werden. Typ ist eine Schnittstelle oder abstrakte Klasse und kann nicht instanziiert werden.
Wie kann ich die List<ITestInterface>
Sammlung deserialisieren ?
Antworten:
Nachfolgend ein ausführliches Beispiel mit dem, was Sie tun möchten:
public interface ITestInterface { string Guid { get; set; } } public class TestClassThatImplementsTestInterface1 : ITestInterface { public string Guid { get; set; } public string Something1 { get; set; } } public class TestClassThatImplementsTestInterface2 : ITestInterface { public string Guid { get; set; } public string Something2 { get; set; } } public class ClassToSerializeViaJson { public ClassToSerializeViaJson() { this.CollectionToSerialize = new List<ITestInterface>(); } public List<ITestInterface> CollectionToSerialize { get; set; } } public class TypeNameSerializationBinder : SerializationBinder { public string TypeFormat { get; private set; } public TypeNameSerializationBinder(string typeFormat) { TypeFormat = typeFormat; } public override void BindToName(Type serializedType, out string assemblyName, out string typeName) { assemblyName = null; typeName = serializedType.Name; } public override Type BindToType(string assemblyName, string typeName) { var resolvedTypeName = string.Format(TypeFormat, typeName); return Type.GetType(resolvedTypeName, true); } } class Program { static void Main() { var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication"); var toserialize = new ClassToSerializeViaJson(); toserialize.CollectionToSerialize.Add( new TestClassThatImplementsTestInterface1() { Guid = Guid.NewGuid().ToString(), Something1 = "Some1" }); toserialize.CollectionToSerialize.Add( new TestClassThatImplementsTestInterface2() { Guid = Guid.NewGuid().ToString(), Something2 = "Some2" }); string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Binder = binder }); var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Binder = binder }); Console.ReadLine(); } }
quelle
ISerializationBinder
anstatt sie zu überschreibenSerializationBinder
. Auch inBindToName
Ich habeserializedType.AssemblyQualifiedName
anstelle von Name verwendet und das übergibt sowohl dasassemblyName
als auch das voll qualifiziertetypeName
an,BindToType
so dass jetzt kein Konstruktor mehr benötigt wird. Dann aktualisieren SieBindToType
mitvar resolvedTypeName = string.Format("{0}, {1}", typeName,assemblyName); and everything should work without providing the namespace & assembly in the constructor
Ich habe diese Frage gefunden, als ich versucht habe, dies selbst zu tun. Nachdem ich die Antwort von Piotr Stapp (Garath) implementiert hatte , war ich beeindruckt, wie einfach es schien. Wenn ich lediglich eine Methode implementiert habe, der bereits der genaue Typ (als Zeichenfolge) übergeben wurde, den ich instanziieren wollte, warum hat die Bibliothek ihn dann nicht automatisch gebunden?
Ich stellte tatsächlich fest, dass ich keine benutzerdefinierten Ordner benötigte. Json.Net konnte genau das tun, was ich brauchte, vorausgesetzt, ich sagte, dass dies das war, was ich tat.
Bei der Serialisierung:
string serializedJson = JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects, TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple });
Bei der De-Serialisierung:
var deserializedObject = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(serializedJson, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Objects });
Relevante Dokumentation: Serialisierungseinstellungen für Json.NET und TypeNameHandling
quelle
Ich war auch überrascht von der Einfachheit in Garaths und kam zu dem Schluss, dass die Json-Bibliothek dies automatisch tun kann. Aber ich dachte auch, dass es noch einfacher ist als die Antwort von Ben Jenkinson (obwohl ich sehen kann, dass es vom Entwickler der json-Bibliothek selbst geändert wurde). Nach meinen Tests müssen Sie TypeNameHandling lediglich wie folgt auf Auto setzen:
var objectToSerialize = new List<IFoo>(); // TODO: Add objects to list var jsonString = JsonConvert.SerializeObject(objectToSerialize, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto }); var deserializedObject = JsonConvert.DeserializeObject<List<IFoo>>(jsonString, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto });
Aus der Dokumentation zur TypeNameHandling-Aufzählung
quelle
TypeNameAssemblyFormat = System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple
Mit den Standardeinstellungen können Sie nicht. JSON.NET kann nicht wissen, wie ein Array deserialisiert wird. Sie können jedoch angeben, welcher Typkonverter für Ihren Schnittstellentyp verwendet werden soll. Informationen dazu finden Sie auf dieser Seite: http://blog.greatrexpectations.com/2012/08/30/deserializing-interface-properties-using-json-net/
Informationen zu diesem Problem finden Sie auch in dieser SO-Frage: Casting-Schnittstellen für die Deserialisierung in JSON.NET
quelle
Dies ist eine alte Frage, aber ich dachte, ich würde eine ausführlichere Antwort hinzufügen (in Form eines Artikels, den ich geschrieben habe): http://skrift.io/articles/archive/bulletproof-interface-deserialization-in-jsonnet /.
TLDR: Anstatt Json.NET so zu konfigurieren, dass Typnamen in das serialisierte JSON eingebettet werden, können Sie mithilfe eines JSON-Konverters herausfinden, welche Klasse mit einer beliebigen benutzerdefinierten Logik deserialisiert werden soll.
Dies hat den Vorteil, dass Sie Ihre Typen umgestalten können, ohne sich Gedanken über das Brechen der Deserialisierung machen zu müssen.
quelle
Dies kann mit den Attributen JSON.NET und JsonSubTypes erfolgen :
[JsonConverter(typeof(JsonSubtypes))] [JsonSubtypes.KnownSubTypeWithProperty(typeof(Test1), "Something1")] [JsonSubtypes.KnownSubTypeWithProperty(typeof(Test2), "Something2")] public interface ITestInterface { string Guid { get; set; } } public class Test1 : ITestInterface { public string Guid { get; set; } public string Something1 { get; set; } } public class Test2 : ITestInterface { public string Guid { get; set; } public string Something2 { get; set; } }
und einfach:
var fromCode = new List<ITestInterface>(); // TODO: Add objects to list var json = JsonConvert.SerializeObject(fromCode); var fromJson = JsonConvert.DeserializeObject<List<ITestInterface>>(json);
quelle
Ich wollte JSON deserialisieren, das von meiner Anwendung nicht serialisiert wurde, daher musste ich die konkrete Implementierung manuell angeben. Ich habe Nicholas 'Antwort erweitert.
Nehmen wir an, wir haben
public class Person { public ILocation Location { get;set; } }
und die konkrete Instanz von
public class Location: ILocation { public string Address1 { get; set; } // etc }
In dieser Klasse hinzufügen
public class ConfigConverter<I, T> : JsonConverter { public override bool CanWrite => false; public override bool CanRead => true; public override bool CanConvert(Type objectType) { return objectType == typeof(I); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new InvalidOperationException("Use default serialization."); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var jsonObject = JObject.Load(reader); var deserialized = (T)Activator.CreateInstance(typeof(T)); serializer.Populate(jsonObject.CreateReader(), deserialized); return deserialized; } }
Definieren Sie dann Ihre Schnittstellen mit dem JsonConverter-Attribut
public class Person { [JsonConverter(typeof(ConfigConverter<ILocation, Location>))] public ILocation Location { get;set; } }
quelle
serializer.Populate
anderejsonObject.ToObject
. Ist da ein Unterschied?Fast ein Duplikat von Inregos Antwort, aber es verdient weitere Erklärung:
Wenn Sie verwenden, enthält
TypeNameHandling.Auto
es nur den Typ- / Baugruppennamen, wenn dies erforderlich ist (dh Schnittstellen und Basis- / abgeleitete Klassen). Ihr JSON ist also sauberer, kleiner und spezifischer.Welches ist nicht eines der Hauptverkaufsargumente für XML / SOAP?
quelle
Vermeiden Sie nach Möglichkeit TypeNameHandling.Auto, insbesondere bei benutzerdefinierbaren Werten.
Sie müssen Ihren eigenen Deserializer für den Sammlungstyp schreiben .
Anstatt andere zu wiederholen, die bereits Boilerplate-Konvertercode veröffentlicht haben (insbesondere Nicholas Westby , dessen Blog-Beitrag sehr nützlich war und oben verlinkt ist ), habe ich die relevanten Änderungen für die Deserialisierung einer Sammlung von Schnittstellen aufgenommen (ich hatte eine Enum-Interface-Eigenschaft zur Unterscheidung von Implementierern ):
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { Collection<T> result = new Collection<T>(); var array = JArray.Load(reader); foreach (JObject jsonObject in array) { var rule = default(T); var value = jsonObject.Value<string>("MyDistinguisher"); MyEnum distinguisher; Enum.TryParse(value, out distinguisher); switch (distinguisher) { case MyEnum.Value1: rule = serializer.Deserialize<Type1>(jsonObject.CreateReader()); break; case MyEnum.Value2: rule = serializer.Deserialize<Type2>(jsonObject.CreateReader()); break; default: rule = serializer.Deserialize<Type3>(jsonObject.CreateReader()); break; } result.Add(rule); } return result; }
Ich hoffe, dies ist hilfreich für die nächste Person, die nach einem Deserializer für die Schnittstellensammlung sucht.
quelle