Saya punya masalah deserializing string json dengan Gson. Saya menerima serangkaian perintah. Perintah dapat dimulai, berhenti, beberapa jenis perintah lainnya. Tentu saya memiliki polimorfisme, dan perintah start / stop mewarisi dari perintah.
Bagaimana saya bisa membuat serial kembali ke objek perintah yang benar menggunakan gson?
Sepertinya saya hanya mendapatkan tipe dasar, yaitu tipe yang dideklarasikan dan tidak pernah tipe runtime.
java
json
polymorphism
gson
deserialization
Sophie
sumber
sumber
Jawaban:
Ini agak terlambat tetapi saya harus melakukan hal yang persis sama hari ini. Jadi, berdasarkan penelitian saya dan ketika menggunakan gson-2.0 Anda benar-benar tidak ingin menggunakan metode registerTypeHierarchyAdapter , melainkan registerTypeAdapter yang lebih biasa . Dan Anda tentu tidak perlu melakukan instanceofs atau menulis adaptor untuk kelas turunan: cukup satu adaptor untuk kelas atau antarmuka dasar, asalkan Anda puas dengan serialisasi default dari kelas turunan. Bagaimanapun, ini kodenya (paket dan impor dihapus) (juga tersedia di github ):
Kelas dasar (antarmuka dalam kasus saya):
Dua kelas turunan, Cat:
Dan Anjing:
IAnimalAdapter:
Dan kelas Tes:
Saat Anda menjalankan Test :: main Anda mendapatkan output berikut:
Saya sebenarnya telah melakukan hal di atas menggunakan metode registerTypeHierarchyAdapter juga, tetapi itu tampaknya memerlukan penerapan kelas serializer / deserializer DogAdapter dan CatAdapter khusus yang sulit dipertahankan kapan pun Anda ingin menambahkan bidang lain ke Dog atau ke Cat.
sumber
Gson saat ini memiliki mekanisme untuk mendaftarkan Adaptor Hierarki Tipe yang dilaporkan dapat dikonfigurasi untuk deserialisasi polimorfik sederhana, tetapi saya tidak melihat bagaimana masalahnya, karena Adaptor Hierarki Tipe tampaknya hanya merupakan pembuat serializer / deserializer / instance gabungan, menyerahkan detail pembuatan instance kepada pembuat kode, tanpa memberikan pendaftaran jenis polimorfik yang sebenarnya.
Sepertinya Gson akan segera memiliki
RuntimeTypeAdapter
deserialisasi polimorfik yang lebih sederhana. Lihat http://code.google.com/p/google-gson/issues/detail?id=231 untuk info lebih lanjut.Jika penggunaan yang baru
RuntimeTypeAdapter
tidak memungkinkan, dan Anda harus menggunakan Gson, maka saya pikir Anda harus menggulirkan solusi Anda sendiri, mendaftarkan deserializer khusus baik sebagai Adaptor Hierarki Tipe atau sebagai Adaptor Tipe. Berikut adalah salah satu contohnya.sumber
RuntimeTypeAdapter
sudah selesai, sayangnya sepertinya belum ada di inti Gson. :-(Marcus Junius Brutus memiliki jawaban yang bagus (terima kasih!). Untuk memperluas contohnya, Anda dapat membuat kelas adaptornya generik untuk bekerja untuk semua jenis objek (Bukan hanya IAnimal) dengan perubahan berikut:
Dan di Kelas Tes:
sumber
GSON memiliki kasus uji yang cukup bagus di sini yang menunjukkan cara menentukan dan mendaftarkan adaptor hierarki tipe.
http://code.google.com/p/google-gson/source/browse/trunk/gson/src/test/java/com/google/gson/functional/TypeHierarchyAdapterTest.java?r=739
Untuk menggunakannya lakukan ini:
Metode serialisasi adaptor bisa menjadi pemeriksaan berjenjang jika-lain dari jenis apa itu serialisasi.
Deserializing agak sedikit hacky. Dalam contoh unit test, ia memeriksa keberadaan atribut tell-tale untuk memutuskan kelas mana yang akan dideserialisasi. Jika Anda dapat mengubah sumber objek yang Anda serialisasi, Anda dapat menambahkan atribut 'classType' ke setiap instance yang menyimpan FQN dari nama kelas instance. Ini sangat tidak berorientasi objek.
sumber
Google telah merilis RuntimeTypeAdapterFactory sendiri untuk menangani polimorfisme tetapi sayangnya ini bukan bagian dari inti gson (Anda harus menyalin dan menempel kelas di dalam proyek Anda).
Contoh:
Di sini saya telah memposting contoh kerja lengkapnya menggunakan model Hewan, Anjing dan Kucing.
Saya pikir lebih baik mengandalkan adaptor ini daripada menerapkannya kembali dari awal.
sumber
Waktu telah lama berlalu, tetapi saya tidak dapat menemukan solusi yang benar-benar bagus secara online .. Berikut ini sedikit perubahan pada solusi @ MarcusJuniusBrutus, yang menghindari rekursi tak terbatas.
Pertahankan deserializer yang sama, tetapi lepaskan serializer -
Kemudian, di kelas asli Anda, tambahkan bidang dengan
@SerializedName("CLASSNAME")
. Triknya sekarang adalah menginisialisasi ini di konstruktor kelas dasar , jadi buat antarmuka Anda menjadi kelas abstrak.Alasan tidak ada rekursi tak terbatas di sini adalah karena kita meneruskan kelas runtime aktual (yaitu, Dog bukan IAnimal) ke
context.deserialize
. Ini tidak akan memanggil adaptor tipe kami, selama kami menggunakanregisterTypeAdapter
dan tidakregisterTypeHierarchyAdapter
sumber
Jawaban yang Diperbarui - Bagian terbaik dari semua jawaban lainnya
Saya menjelaskan solusi untuk berbagai kasus penggunaan dan akan menangani masalah rekursi tak terbatas juga
Kasus 1: Anda mengendalikan kelas , yaitu, Anda bisa menulis kelas Anda sendiri
Cat
,Dog
serta kelasIAnimal
antarmuka. Anda cukup mengikuti solusi yang disediakan oleh @ marcus-junius-brutus (jawaban teratas)Tidak akan ada rekursi tak terbatas jika ada antarmuka dasar yang sama seperti
IAnimal
Tetapi, bagaimana jika saya tidak ingin mengimplementasikan
IAnimal
atau antarmuka semacam itu?Kemudian, @ marcus-junius-brutus (jawaban teratas) akan menghasilkan kesalahan rekursi tak terbatas. Dalam hal ini, kita dapat melakukan sesuatu seperti di bawah ini.
Kita harus membuat konstruktor salinan di dalam kelas dasar dan subkelas pembungkus sebagai berikut:
.
Dan serializer untuk tipe
Cat
:Jadi, mengapa pembuat salinan?
Nah, setelah Anda menentukan konstruktor salinan, tidak peduli seberapa banyak kelas dasar berubah, pembungkus Anda akan melanjutkan dengan peran yang sama. Kedua, jika kita tidak mendefinisikan konstruktor salinan dan hanya membuat subkelas kelas dasar maka kita harus "berbicara" dalam istilah kelas yang diperluas, yaitu,
CatWrapper
. Sangat mungkin bahwa komponen Anda berbicara dalam istilah kelas dasar dan bukan jenis pembungkusnya.Apakah ada alternatif yang mudah?
Tentu, sekarang telah diperkenalkan oleh Google - inilah
RuntimeTypeAdapterFactory
implementasinya:Di sini, Anda perlu memasukkan
Animal
kolom yang disebut "type" in dan nilai di dalamnya yang samaDog
menjadi "dog",Cat
menjadi "cat"Contoh lengkap: https://static.javadoc.io/org.danilopianini/gson-extras/0.2.1/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.html
Kasus 2: Anda tidak mengontrol kelas . Anda bergabung dengan perusahaan atau menggunakan pustaka di mana kelas sudah ditentukan dan manajer Anda tidak ingin Anda mengubahnya dengan cara apa pun - Anda dapat membuat subkelas kelas Anda dan meminta mereka menerapkan antarmuka penanda umum (yang tidak memiliki metode apa pun ) seperti
AnimalInterface
.Ex:
.
Jadi, kami akan menggunakan
CatWrapper
alih- alihCat
,DogWrapper
alih- alihDog
danAlternativeAnimalAdapter
alih-alihIAnimalAdapter
Kami melakukan tes:
Keluaran:
sumber
Jika Anda ingin mengelola TypeAdapter untuk sebuah tipe dan yang lainnya untuk sub tipe-nya, Anda dapat menggunakan TypeAdapterFactory seperti ini:
Pabrik ini akan mengirimkan TypeAdapter yang paling akurat
sumber
Jika Anda menggabungkan jawaban Marcus Junius Brutus dengan edit user2242263, Anda dapat menghindari keharusan menentukan hierarki kelas yang besar di adaptor Anda dengan mendefinisikan adaptor Anda sebagai bekerja pada tipe antarmuka. Anda kemudian dapat memberikan implementasi default toJSON () dan fromJSON () di antarmuka Anda (yang hanya menyertakan dua metode ini) dan meminta setiap kelas yang Anda butuhkan untuk membuat serialisasi mengimplementasikan antarmuka Anda. Untuk menangani casting, di subclass Anda, Anda dapat menyediakan metode fromJSON () statis yang deserialisasi dan melakukan transmisi yang sesuai dari jenis antarmuka Anda. Ini bekerja dengan sangat baik untuk saya (berhati-hatilah dengan membuat serial / deserialisasi kelas yang berisi hashmaps - tambahkan ini saat Anda membuat instantiate gson builder Anda:
GsonBuilder builder = new GsonBuilder().enableComplexMapKeySerialization();
Semoga ini membantu seseorang menghemat waktu dan tenaga!
sumber