Saya mengalami masalah dengan deserializer khusus saya di Jackson. Saya ingin mengakses serializer default untuk mengisi objek yang saya deserialisasi. Setelah populasi saya akan melakukan beberapa hal khusus tetapi pertama-tama saya ingin deserialize objek dengan perilaku Jackson default.
Ini adalah kode yang saya miliki saat ini.
public class UserEventDeserializer extends StdDeserializer<User> {
private static final long serialVersionUID = 7923585097068641765L;
public UserEventDeserializer() {
super(User.class);
}
@Override
@Transactional
public User deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
ObjectCodec oc = jp.getCodec();
JsonNode node = oc.readTree(jp);
User deserializedUser = null;
deserializedUser = super.deserialize(jp, ctxt, new User());
// The previous line generates an exception java.lang.UnsupportedOperationException
// Because there is no implementation of the deserializer.
// I want a way to access the default spring deserializer for my User class.
// How can I do that?
//Special logic
return deserializedUser;
}
}
Yang saya perlukan adalah cara untuk menginisialisasi deserializer default sehingga saya dapat mengisi POJO saya terlebih dahulu sebelum memulai logika khusus.
Ketika memanggil deserialize dari dalam custom deserializer Tampaknya metode ini dipanggil dari konteks saat ini tidak peduli bagaimana saya membangun kelas serializer. Karena anotasi di POJO saya. Ini menyebabkan pengecualian Stack Overflow karena alasan yang jelas.
Saya telah mencoba menginisialisasi a BeanDeserializer
tetapi prosesnya sangat rumit dan saya belum berhasil menemukan cara yang tepat untuk melakukannya. Saya juga telah mencoba membebani secara berlebihan namun AnnotationIntrospector
tidak berhasil, karena berpikir bahwa hal itu dapat membantu saya mengabaikan anotasi di DeserializerContext
. Akhirnya, saya mungkin telah berhasil menggunakan JsonDeserializerBuilders
meskipun ini mengharuskan saya untuk melakukan beberapa hal ajaib untuk mendapatkan konteks aplikasi dari Spring. Saya akan menghargai hal apa pun yang dapat membawa saya ke solusi yang lebih bersih, misalnya bagaimana saya dapat membuat konteks deserialisasi tanpa membaca JsonDeserializer
anotasi.
DeserializationContext
bukanlah sesuatu yang harus Anda buat atau ubah; itu akan disediakan olehObjectMapper
.AnnotationIntrospector
, juga, tidak akan membantu mendapatkan akses.Jawaban:
Seperti yang telah disarankan oleh StaxMan, Anda dapat melakukan ini dengan menulis a
BeanDeserializerModifier
dan mendaftarkannya melaluiSimpleModule
. Contoh berikut seharusnya berfungsi:sumber
JsonSerializer
? Saya memiliki beberapa serializers tetapi mereka memiliki kode yang sama jadi saya ingin membuatnya. Saya mencoba untuk langsung memanggil The serializer tetapi Hasilnya tidak terbungkus dalam hasil JSON (setiap panggilan serializer membuat objek baru)BeanSerializerModifier
,ResolvableSerializer
danContextualSerializer
merupakan antarmuka yang cocok untuk digunakan dalam serialisasi.readTree()
tetapi jawabannya tidak. Apa keuntungan dari pendekatan ini dibandingkan dengan yang diposting oleh Derek Cochran ? Apakah ada cara untuk membuatnya berhasilreadTree()
?Saya menemukan jawaban di ans yang jauh lebih mudah dibaca daripada jawaban yang diterima.
Ini benar-benar tidak lebih mudah dari ini.
sumber
StackOverflowError
, karena Jackson akan menggunakan serializer yang sama lagi untukUser
...The
DeserializationContext
memilikireadValue()
metode Anda dapat menggunakan. Ini harus bekerja untuk deserializer default dan deserializer kustom yang Anda miliki.Hanya pastikan untuk menelepon
traverse()
padaJsonNode
tingkat Anda ingin membaca untuk mengambilJsonParser
untuk lolos kereadValue()
.sumber
Ada beberapa cara untuk melakukan ini, tetapi melakukannya dengan benar membutuhkan lebih banyak pekerjaan. Pada dasarnya Anda tidak dapat menggunakan sub-classing, karena kebutuhan deserializers default informasi dibangun dari definisi kelas.
Jadi yang paling mungkin Anda gunakan adalah membuat
BeanDeserializerModifier
, mendaftarkannya melaluiModule
antarmuka (gunakanSimpleModule
). Anda perlu mendefinisikan / menimpamodifyDeserializer
, dan untuk kasus tertentu di mana Anda ingin menambahkan logika Anda sendiri (di mana jenisnya cocok), buat deserializer Anda sendiri, berikan deserializer default yang Anda berikan. Dan kemudian dalamdeserialize()
metode Anda bisa mendelegasikan panggilan, mengambil Object hasil.Alternatifnya, jika Anda benar-benar harus membuat dan mengisi objek, Anda dapat melakukannya dan memanggil versi overloaded
deserialize()
yang membutuhkan argumen ketiga; keberatan untuk melakukan deserialisasi.Cara lain yang mungkin berhasil (tetapi tidak 100% yakin) adalah dengan menetapkan
Converter
object (@JsonDeserialize(converter=MyConverter.class)
). Ini adalah fitur Jackson 2.2 baru. Dalam kasus Anda, Konverter tidak akan benar-benar mengonversi jenis, tetapi menyederhanakan modifikasi objek: tetapi saya tidak tahu apakah itu akan memungkinkan Anda melakukan apa yang Anda inginkan, karena deserializer default akan dipanggil terlebih dahulu, dan baru kemudian AndaConverter
.sumber
BeanDeserializerModifier
adalah penangan panggilan balik yang memungkinkannya.Jika memungkinkan bagi Anda untuk mendeklarasikan kelas Pengguna tambahan, maka Anda dapat menerapkannya hanya dengan menggunakan anotasi
sumber
Sejalan dengan apa yang disarankan Tomáš Záluský , dalam kasus di mana penggunaan
BeanDeserializerModifier
tidak diinginkan, Anda dapat membuat sendiri deserializer default menggunakanBeanDeserializerFactory
, meskipun ada beberapa pengaturan tambahan yang diperlukan. Dalam konteksnya, solusi ini akan terlihat seperti ini:sumber
Berikut adalah satu perjalanan menggunakan ObjectMapper
Dan tolong: Benar-benar tidak perlu menggunakan nilai String atau yang lainnya. Semua informasi yang dibutuhkan diberikan oleh JsonParser, jadi gunakanlah.
sumber
Saya tidak setuju dengan menggunakan
BeanSerializerModifier
karena memaksa untuk menyatakan beberapa perubahan perilaku di pusatObjectMapper
daripada di deserializer kustom itu sendiri dan sebenarnya itu adalah solusi paralel untuk membuat anotasi kelas entitas denganJsonSerialize
. Jika Anda merasakan hal yang sama, Anda mungkin menghargai jawaban saya di sini: https://stackoverflow.com/a/43213463/653539sumber
Solusi yang lebih sederhana bagi saya adalah dengan menambahkan kacang lain
ObjectMapper
dan menggunakannya untuk menghilangkan nama objek (terima kasih kepada https://stackoverflow.com/users/1032167/varren komentar) - dalam kasus saya, saya tertarik untuk melakukan deserialisasi ke id-nya (int) atau seluruh objek https://stackoverflow.com/a/46618193/986160untuk setiap entitas yang perlu melalui deserializer khusus, kita perlu mengkonfigurasinya di
ObjectMapper
kacang global Aplikasi Spring Boot dalam kasus saya (misalnya untukCategory
):sumber
Anda pasti akan gagal jika mencoba membuat deserializer kustom dari awal.
Sebagai gantinya, Anda perlu mendapatkan instance deserializer default (dikonfigurasi sepenuhnya) melalui custom
BeanDeserializerModifier
, lalu meneruskan instance ini ke kelas deserializer kustom Anda:Catatan: Pendaftaran modul ini menggantikan
@JsonDeserialize
anotasi, yaituUser
kelas atauUser
bidang tidak lagi diberi anotasi dengan anotasi ini.Deserializer kustom kemudian harus didasarkan pada
DelegatingDeserializer
sehingga semua metode didelegasikan, kecuali Anda menyediakan implementasi eksplisit:sumber
Menggunakan
BeanDeserializerModifier
berfungsi dengan baik, tetapi jika Anda perlu menggunakannya,JsonDeserialize
ada cara untuk melakukannya denganAnnotationIntrospector
seperti ini:Sekarang mapper yang disalin sekarang akan mengabaikan deserializer kustom Anda (MyDeserializer.class) dan menggunakan implementasi default. Anda dapat menggunakannya di dalam
deserialize
metode deserializer khusus Anda untuk menghindari rekursi dengan membuat mapper yang disalin menjadi statis atau menyambungkannya jika menggunakan Spring.sumber