Saya mencoba menyiapkan pembaca yang akan mengambil objek JSON dari berbagai situs web (pikirkan informasi scraping) dan menerjemahkannya ke dalam objek C #. Saya saat ini menggunakan JSON.NET untuk proses deserialization. Masalah yang saya hadapi adalah bahwa ia tidak tahu bagaimana menangani properti tingkat antarmuka di kelas. Jadi sesuatu yang alami:
public IThingy Thing
Akan menghasilkan kesalahan:
Tidak dapat membuat instance berjenis IThingy. Type adalah antarmuka atau kelas abstrak dan tidak dapat dipakai.
Relatif penting untuk menjadikannya sebagai IThingy dibandingkan dengan Thingy karena kode yang saya kerjakan dianggap sensitif dan pengujian unit sangat penting. Mocking objek untuk skrip pengujian atom tidak dimungkinkan dengan objek yang lengkap seperti Thingy. Mereka harus menjadi antarmuka.
Saya telah mempelajari dokumentasi JSON.NET untuk sementara waktu sekarang, dan pertanyaan yang dapat saya temukan di situs ini terkait dengan ini semuanya berasal lebih dari setahun yang lalu. Ada bantuan?
Selain itu, jika penting, aplikasi saya ditulis dalam .NET 4.0.
Jawaban:
@SamualDavis memberikan solusi bagus untuk pertanyaan terkait , yang akan saya rangkum di sini.
Jika Anda harus deserialisasi aliran JSON menjadi kelas beton yang memiliki properti antarmuka, Anda dapat menyertakan kelas konkret sebagai parameter ke konstruktor untuk kelas tersebut! NewtonSoft deserializer cukup pintar untuk mengetahui bahwa ia perlu menggunakan kelas konkret tersebut untuk deserialisasi properti.
Berikut ini contohnya:
sumber
[JsonConstructor]
atribut.(Disalin dari pertanyaan ini )
Dalam kasus di mana saya tidak memiliki kendali atas JSON yang masuk (sehingga tidak dapat memastikan bahwa itu termasuk properti $ type) saya telah menulis konverter khusus yang hanya memungkinkan Anda untuk secara eksplisit menentukan jenis konkret:
Ini hanya menggunakan implementasi serializer default dari Json.Net sementara secara eksplisit menentukan tipe konkret.
Ringkasan tersedia di posting blog ini . Kode sumber di bawah ini:
sumber
ConcreteListTypeConverter<TInterface, TImplementation>
untuk menangani anggota kelas tipeIList<TInterface>
.concreteTypeConverter
pertanyaan tersebut.ConcreteListTypeConverter<TInterface, TImplementation>
implementasi Anda ?Mengapa menggunakan konverter? Ada fungsi asli
Newtonsoft.Json
untuk menyelesaikan masalah ini dengan tepat:Set
TypeNameHandling
diJsonSerializerSettings
keTypeNameHandling.Auto
Ini akan menempatkan setiap tipe ke dalam json, yang tidak diadakan sebagai contoh konkret dari sebuah tipe tetapi sebagai antarmuka atau kelas abstrak.
Pastikan Anda menggunakan pengaturan yang sama untuk serialisasi dan deserialisasi .
Saya mengujinya, dan itu bekerja seperti pesona, bahkan dengan daftar.
Hasil Pencarian Hasil Web dengan link situs
⚠️ PERINGATAN :
Gunakan ini hanya untuk json dari sumber yang dikenal dan tepercaya. Snipsnipsnip pengguna dengan benar menyebutkan bahwa ini memang vunerabilitas.
Lihat CA2328 dan SCS0028 untuk informasi lebih lanjut.
Sumber dan implementasi manual alternatif: Code Inside Blog
sumber
Untuk mengaktifkan deserialisasi beberapa implementasi antarmuka, Anda dapat menggunakan JsonConverter, tetapi tidak melalui atribut:
DTOJsonConverter memetakan setiap antarmuka dengan implementasi konkret:
DTOJsonConverter diperlukan hanya untuk deserializer. Proses serialisasi tidak berubah. Objek Json tidak perlu menyematkan nama tipe konkret.
Posting SO ini menawarkan solusi yang sama selangkah lebih maju dengan JsonConverter generik.
sumber
FullName
jika Anda dapat membandingkan tipe secara langsung?Gunakan kelas ini, untuk memetakan tipe abstrak ke tipe nyata:
... dan saat deserialize:
sumber
where TReal : TAbstract
untuk memastikannya dapat mentransmisikan ke tipewhere TReal : class, TAbstract, new()
.Nicholas Westby memberikan solusi hebat dalam artikel yang luar biasa .
Jika Anda ingin Deserializing JSON ke salah satu dari banyak kemungkinan kelas yang mengimplementasikan antarmuka seperti itu:
Anda dapat menggunakan konverter JSON khusus:
Dan Anda perlu menghias properti "Profesi" dengan atribut JsonConverter agar tahu cara menggunakan konverter khusus Anda:
Dan kemudian, Anda dapat mentransmisikan kelas Anda dengan Antarmuka:
sumber
Dua hal yang bisa Anda coba:
Menerapkan model coba / parse:
Atau, jika Anda dapat melakukannya dalam model objek Anda, terapkan kelas dasar konkret antara IPerson dan objek daun Anda, dan deserialisasi padanya.
Yang pertama berpotensi gagal pada waktu proses, yang kedua memerlukan perubahan pada model objek Anda dan menghomogenkan keluaran ke penyebut umum terendah.
sumber
Saya menemukan ini berguna. Anda mungkin juga.
Contoh Penggunaan
Konverter Pembuatan Kustom
Dokumentasi Json.NET
sumber
Bagi mereka yang mungkin penasaran dengan ConcreteListTypeConverter yang direferensikan oleh Oliver, berikut adalah upaya saya:
sumber
CanConvert(Type objectType) { return true;}
. Tampaknya hacky, bagaimana sebenarnya ini membantu? Saya mungkin salah, tetapi bukankah itu seperti memberi tahu petarung kecil yang tidak berpengalaman bahwa mereka akan memenangkan pertarungan, tidak peduli lawannya?Untuk apa nilainya, saya akhirnya harus menangani ini sendiri untuk sebagian besar. Setiap objek memiliki a Deserialize (string jsonStream) . Beberapa cuplikannya:
Dalam hal ini, Thingy baru (string) adalah konstruktor yang akan memanggil metode Deserialize (string jsonStream) dari jenis beton yang sesuai. Skema ini akan terus berlanjut ke bawah dan ke bawah sampai Anda mencapai titik dasar yang bisa ditangani oleh json.NET.
Begitu seterusnya. Pengaturan ini memungkinkan saya untuk memberikan pengaturan json.NET yang dapat ditangani tanpa harus merefaktor sebagian besar pustaka itu sendiri atau menggunakan model coba / parse yang berat yang akan menghambat seluruh pustaka kami karena jumlah objek yang terlibat. Ini juga berarti bahwa saya dapat secara efektif menangani perubahan json apa pun pada objek tertentu, dan saya tidak perlu khawatir tentang semua yang disentuh objek. Ini sama sekali bukan solusi ideal, tetapi berfungsi dengan baik dari pengujian unit dan integrasi kami.
sumber
Misalkan pengaturan autofac seperti berikut:
Kemudian, misalkan kelas Anda seperti ini:
Oleh karena itu, penggunaan resolver dalam deserialization bisa seperti:
Anda dapat melihat detail lebih lanjut di http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm
sumber
Tidak ada objek yang akan pernah menjadi sebuah IThingy sebagai antarmuka semua abstrak dengan definisi.
Objek yang Anda miliki yang pertama kali dibuat berseri adalah dari beberapa jenis konkret , menerapkan antarmuka abstrak . Anda harus memiliki beton yang sama kelas menghidupkan kembali data serial.
Objek yang dihasilkan kemudian akan dari beberapa jenis yang mengimplementasikan yang abstrak antarmuka yang Anda cari.
Dari dokumentasi berikut yang dapat Anda gunakan
saat deserializing untuk menginformasikan JSON.NET tentang jenis konkret.
sumber
_type
properti yang menandakan tipe beton yang akan digunakan.Solusi saya untuk yang satu ini, yang saya suka karena cukup umum, adalah sebagai berikut:
}
Anda dapat dengan jelas dan mudah mengubahnya menjadi konverter yang lebih umum dengan menambahkan konstruktor yang mengambil argumen tipe Dictionary <Type, Type> yang dapat digunakan untuk membuat instance variabel instance konversi.
sumber
Beberapa tahun kemudian saya mengalami masalah serupa. Dalam kasus saya ada banyak antarmuka bersarang dan preferensi untuk menghasilkan kelas beton pada waktu proses sehingga akan bekerja dengan kelas generik.
Saya memutuskan untuk membuat kelas proxy pada waktu proses yang membungkus objek yang dikembalikan oleh Newtonsoft.
Keuntungan dari pendekatan ini adalah tidak memerlukan implementasi konkret kelas dan dapat menangani kedalaman antarmuka bersarang secara otomatis. Anda dapat melihat lebih banyak tentang itu di blog saya .
Pemakaian:
sumber
PopulateObject
proksi yang dihasilkan oleh Antarmuka Dadakan. Sayangnya saya menyerah untuk Mengetik Bebek - itu hanya lebih mudah untuk membuat Serializer Kontrak Json khusus yang menggunakan refleksi untuk menemukan implementasi yang ada dari antarmuka yang diminta dan menggunakannya.Gunakan JsonKnownTypes ini , cara penggunaannya sangat mirip, hanya menambahkan diskriminator ke json:
Sekarang ketika Anda membuat serial objek di json akan ditambahkan
"$type"
dengan"myClass"
nilai dan itu akan digunakan untuk deserializeJson:
sumber
Solusi saya menambahkan elemen antarmuka di konstruktor.
sumber