Saya memiliki definisi kelas yang berisi properti yang mengembalikan antarmuka.
public class Foo
{
public int Number { get; set; }
public ISomething Thing { get; set; }
}
Mencoba untuk membuat serial kelas Foo menggunakan Json.NET memberi saya pesan kesalahan seperti, "Tidak dapat membuat contoh jenis 'ISomething'. ISomething mungkin sebuah antarmuka atau kelas abstrak."
Apakah ada atribut atau konverter Json.NET yang memungkinkan saya menentukan Something
kelas konkret untuk digunakan selama deserialisasi?
.net
serialization
json.net
dthrasher.dll
sumber
sumber
Jawaban:
Salah satu hal yang dapat Anda lakukan dengan Json.NET adalah:
var settings = new JsonSerializerSettings(); settings.TypeNameHandling = TypeNameHandling.Objects; JsonConvert.SerializeObject(entity, Formatting.Indented, settings);
The
TypeNameHandling
bendera akan menambahkan$type
properti ke JSON, yang memungkinkan Json.NET untuk mengetahui jenis beton perlu deserialize objek ke dalam. Ini memungkinkan Anda untuk melakukan deserialisasi objek sambil tetap memenuhi antarmuka atau kelas dasar abstrak.Sisi negatifnya, bagaimanapun, adalah bahwa ini sangat spesifik Json.NET. Ini
$type
akan menjadi tipe yang sepenuhnya memenuhi syarat, jadi jika Anda membuat serial dengan info tipe ,, deserializer harus bisa memahaminya juga.Dokumentasi: Pengaturan Serialisasi dengan Json.NET
sumber
TypeNameHandling
. Lihat hati-hati TypeNameHandling di Newtonsoft Json untuk detailnya.Anda dapat mencapai ini melalui penggunaan kelas JsonConverter. Misalkan Anda memiliki kelas dengan properti antarmuka;
public class Organisation { public string Name { get; set; } [JsonConverter(typeof(TycoonConverter))] public IPerson Owner { get; set; } } public interface IPerson { string Name { get; set; } } public class Tycoon : IPerson { public string Name { get; set; } }
JsonConverter Anda bertanggung jawab untuk membuat serial dan membatalkan serialisasi properti yang mendasarinya;
public class TycoonConverter : JsonConverter { public override bool CanConvert(Type objectType) { return (objectType == typeof(IPerson)); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { return serializer.Deserialize<Tycoon>(reader); } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { // Left as an exercise to the reader :) throw new NotImplementedException(); } }
Saat Anda bekerja dengan Organisasi yang dideserialisasi melalui Json.Net, orang yang mendasari untuk properti Pemilik adalah dari tipe Tycoon.
sumber
Alih-alih meneruskan objek JsonSerializerSettings yang disesuaikan ke JsonConvert.SerializeObject () dengan opsi TypeNameHandling.Objects, seperti yang disebutkan sebelumnya, Anda cukup menandai properti antarmuka tertentu dengan atribut sehingga JSON yang dihasilkan tidak akan membengkak dengan properti "$ type" pada SETIAP objek:
public class Foo { public int Number { get; set; } // Add "$type" property containing type info of concrete class. [JsonProperty( TypeNameHandling = TypeNameHandling.Objects )] public ISomething { get; set; } }
sumber
Dalam versi terbaru dari konverter Newtonsoft Json pihak ketiga Anda dapat mengatur konstruktor dengan tipe konkret yang berhubungan dengan properti antarmuka.
public class Foo { public int Number { get; private set; } public ISomething IsSomething { get; private set; } public Foo(int number, Something concreteType) { Number = number; IsSomething = concreteType; } }
Selama Sesuatu mengimplementasikan IS, sesuatu ini harus bekerja. Juga jangan letakkan konstruktor kosong default jika konverter JSon mencoba menggunakan itu, Anda harus memaksanya untuk menggunakan konstruktor yang berisi tipe beton.
PS. ini juga memungkinkan Anda untuk menjadikan penyetel Anda pribadi.
sumber
Punya masalah yang sama jadi saya datang dengan Konverter saya sendiri yang menggunakan argumen tipe yang dikenal.
public class JsonKnownTypeConverter : JsonConverter { public IEnumerable<Type> KnownTypes { get; set; } public JsonKnownTypeConverter(IEnumerable<Type> knownTypes) { KnownTypes = knownTypes; } protected object Create(Type objectType, JObject jObject) { if (jObject["$type"] != null) { string typeName = jObject["$type"].ToString(); return Activator.CreateInstance(KnownTypes.First(x =>typeName.Contains("."+x.Name+","))); } throw new InvalidOperationException("No supported type"); } public override bool CanConvert(Type objectType) { if (KnownTypes == null) return false; return (objectType.IsInterface || objectType.IsAbstract) && KnownTypes.Any(objectType.IsAssignableFrom); } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { // Load JObject from stream JObject jObject = JObject.Load(reader); // Create target object based on JObject var target = Create(objectType, jObject); // Populate the object properties serializer.Populate(jObject.CreateReader(), target); return target; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { throw new NotImplementedException(); } }
Saya mendefinisikan dua metode ekstensi untuk deserialisasi dan serialisasi:
public static class AltiJsonSerializer { public static T DeserializeJson<T>(this string jsonString, IEnumerable<Type> knownTypes = null) { if (string.IsNullOrEmpty(jsonString)) return default(T); return JsonConvert.DeserializeObject<T>(jsonString, new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Converters = new List<JsonConverter> ( new JsonConverter[] { new JsonKnownTypeConverter(knownTypes) } ) } ); } public static string SerializeJson(this object objectToSerialize) { return JsonConvert.SerializeObject(objectToSerialize, Formatting.Indented, new JsonSerializerSettings {TypeNameHandling = TypeNameHandling.Auto}); } }
Anda dapat menentukan cara Anda sendiri untuk membandingkan dan mengidentifikasi tipe dalam konversi, saya hanya menggunakan nama kelas.
sumber
Biasanya saya selalu menggunakan solusi dengan
TypeNameHandling
seperti yang disarankan oleh DanielT, tetapi dalam kasus di sini saya tidak memiliki kendali atas JSON yang masuk (dan karenanya tidak dapat memastikan bahwa itu termasuk$type
properti) Saya telah menulis konverter khusus yang hanya memungkinkan Anda untuk menentukan secara eksplisit jenis beton:public class Model { [JsonConverter(typeof(ConcreteTypeConverter<Something>))] public ISomething TheThing { get; set; } }
Ini hanya menggunakan implementasi serializer default dari Json.Net sementara secara eksplisit menentukan tipe konkret.
Kode sumber dan ikhtisar tersedia di posting blog ini .
sumber
Saya hanya ingin melengkapi contoh yang @Daniel T. tunjukkan di atas:
Jika Anda menggunakan kode ini untuk membuat serial objek Anda:
var settings = new JsonSerializerSettings(); settings.TypeNameHandling = TypeNameHandling.Objects; JsonConvert.SerializeObject(entity, Formatting.Indented, settings);
Kode untuk deserialisasi json akan terlihat seperti ini:
var settings = new JsonSerializerSettings(); settings.TypeNameHandling = TypeNameHandling.Objects; var entity = JsonConvert.DeserializeObject<EntityType>(json, settings);
Beginilah cara json menyesuaikan diri saat menggunakan
TypeNameHandling
bendera:sumber
Saya bertanya-tanya tentang hal yang sama ini, tetapi saya khawatir itu tidak dapat dilakukan.
Mari kita lihat seperti ini. Anda menyerahkan ke JSon.net serangkaian data, dan jenis untuk deserialisasi. Apa yang harus dilakukan JSON.net ketika menemukan ISomething itu? Ia tidak dapat membuat tipe baru dari ISsesuatu karena ISomething bukanlah sebuah objek. Ia juga tidak dapat membuat objek yang mengimplementasikan ISomething, karena ia tidak memiliki petunjuk yang mana dari banyak objek yang mungkin mewarisi ISsesuatu yang harus digunakan. Antarmuka, adalah sesuatu yang dapat diserialkan secara otomatis, tetapi tidak otomatis dinonaktifkan.
Apa yang akan saya lakukan adalah melihat menggantikan ISsesuatu dengan kelas dasar. Menggunakan itu Anda mungkin bisa mendapatkan efek yang Anda cari.
sumber
Berikut ini referensi ke artikel yang ditulis oleh ScottGu
Berdasarkan itu, saya menulis beberapa kode yang menurut saya mungkin bisa membantu
public interface IEducationalInstitute { string Name { get; set; } } public class School : IEducationalInstitute { private string name; #region IEducationalInstitute Members public string Name { get { return name; } set { name = value; } } #endregion } public class Student { public IEducationalInstitute LocalSchool { get; set; } public int ID { get; set; } } public static class JSONHelper { public static string ToJSON(this object obj) { JavaScriptSerializer serializer = new JavaScriptSerializer(); return serializer.Serialize(obj); } public static string ToJSON(this object obj, int depth) { JavaScriptSerializer serializer = new JavaScriptSerializer(); serializer.RecursionLimit = depth; return serializer.Serialize(obj); } }
Dan begitulah Anda menyebutnya
School myFavSchool = new School() { Name = "JFK High School" }; Student sam = new Student() { ID = 1, LocalSchool = myFavSchool }; string jSONstring = sam.ToJSON(); Console.WriteLine(jSONstring); //Result {"LocalSchool":{"Name":"JFK High School"},"ID":1}
Jika saya memahaminya dengan benar, saya rasa Anda tidak perlu menentukan kelas konkret yang mengimplementasikan antarmuka untuk serialisasi JSON.
sumber