1. Pengenalan
Menyalurkan struktur data lengkap kami kepada JSON menggunakan perwakilan satu persatu semua bidang mungkin tidak sesuai kadang-kadang atau mungkin tidak seperti yang kita mahukan. Sebaliknya, kami mungkin ingin membuat pandangan luas atau dipermudahkan data kami. Di sinilah alat penyusun Jackson khas dimainkan.
Walau bagaimanapun, melaksanakan penyesuai khusus boleh membosankan, terutama jika objek model kami mempunyai banyak bidang, koleksi, atau objek bersarang. Nasib baik, perpustakaan Jackson mempunyai beberapa peruntukan yang dapat menjadikan pekerjaan ini lebih mudah.
Dalam tutorial ringkas ini, kita akan melihat serializer Jackson tersuai dan menunjukkan cara mengakses serializer lalai di dalam serializer custom .
2. Contoh Model Data
Sebelum kita menyelidiki penyesuaian Jackson, mari kita lihat contoh kelas Folder yang ingin kita bersiri:
public class Folder { private Long id; private String name; private String owner; private Date created; private Date modified; private Date lastAccess; private List files = new ArrayList(); // standard getters and setters }
Dan kelas Fail , yang ditakrifkan sebagai Daftar di dalam kelas Folder kami :
public class File { private Long id; private String name; // standard getters and setters }
3. Serializer Custom di Jackson
Kelebihan utama menggunakan penyesuai khas ialah kita tidak perlu mengubah struktur kelas kita. Selain itu, kita dapat dengan mudah melepaskan tingkah laku yang diharapkan dari kelas itu sendiri.
Oleh itu, mari kita bayangkan bahawa kita mahu pandangan kelas Folder kita yang dikurangkan :
{ "name": "Root Folder", "files": [ {"id": 1, "name": "File 1"}, {"id": 2, "name": "File 2"} ] }
Seperti yang akan kita lihat pada bahagian seterusnya, terdapat beberapa cara untuk mencapai hasil yang kita mahukan di Jackson.
3.1. Pendekatan Brute Force
Pertama, tanpa menggunakan serializer lalai Jackson, kita dapat membuat serializer khas di mana kita melakukan semua pengangkatan berat.
Mari buat serializer khas untuk kelas Folder kami untuk mencapainya:
public class FolderJsonSerializer extends StdSerializer { public FolderJsonSerializer() { super(Folder.class); } @Override public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); gen.writeStringField("name", value.getName()); gen.writeArrayFieldStart("files"); for (File file : value.getFiles()) { gen.writeStartObject(); gen.writeNumberField("id", file.getId()); gen.writeStringField("name", file.getName()); gen.writeEndObject(); } gen.writeEndArray(); gen.writeEndObject(); } }
Oleh itu, kita dapat membuat siri kelas Folder kita ke paparan yang lebih rendah yang hanya mengandungi bidang yang kita mahukan.
3.2. Menggunakan ObjectMapper Dalaman
Walaupun penyusun bersiri khusus memberi kami fleksibiliti untuk mengubah setiap harta secara terperinci, kami dapat mempermudah tugas kami dengan menggunakan semula alat penyusun lalai Jackson.
Salah satu cara menggunakan serializer lalai adalah dengan mengakses kelas ObjectMapper dalaman :
@Override public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); gen.writeStringField("name", value.getName()); ObjectMapper mapper = (ObjectMapper) gen.getCodec(); gen.writeFieldName("files"); String stringValue = mapper.writeValueAsString(value.getFiles()); gen.writeRawValue(stringValue); gen.writeEndObject(); }
Jadi, Jackson hanya mengendalikan mengangkat berat oleh serializing yang List of Fail objek, dan kemudian output kami akan sama.
3.3. Menggunakan SerializerProvider
Kaedah lain untuk memanggil serializer lalai adalah menggunakan SerializerProvider. Oleh itu, kami mendelegasikan proses tersebut ke serializer lalai dari jenis File .
Sekarang, mari kita permudahkan kod kita sedikit dengan bantuan SerializerProvider :
@Override public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); gen.writeStringField("name", value.getName()); provider.defaultSerializeField("files", value.getFiles(), gen); gen.writeEndObject(); }
Dan, seperti sebelumnya, kami mendapat output yang sama.
4. Kemungkinan Masalah Berulang
Bergantung pada kes penggunaan, kami mungkin perlu memperluas data bersiri kami dengan memasukkan lebih banyak butiran untuk Folder . Ini mungkin untuk sistem warisan atau aplikasi luaran yang disatukan yang tidak berpeluang kita ubah .
Mari ubah serializer kami untuk membuat medan perincian untuk data bersiri kami untuk hanya mendedahkan semua bidang kelas Folder :
@Override public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); gen.writeStringField("name", value.getName()); provider.defaultSerializeField("files", value.getFiles(), gen); // this line causes exception provider.defaultSerializeField("details", value, gen); gen.writeEndObject(); }
Kali ini kita mendapat pengecualian StackOverflowError .
Apabila kita menentukan serializer tersuai, Jackson secara dalaman menggantikan contoh BeanSerializer asli yang dibuat untuk jenis Folder . Akibatnya, SerializerProvider kami mencari serializer yang disesuaikan setiap masa, bukan yang default, dan ini menyebabkan gelung tidak terhingga.
Jadi, bagaimana kita menyelesaikan masalah ini? Kami akan melihat satu penyelesaian yang boleh digunakan untuk senario ini di bahagian seterusnya.
5. Menggunakan BeanSerializerModifier
Penyelesaian yang mungkin dilakukan adalah menggunakan BeanSerializerModifier untuk menyimpan serializer lalai untuk jenis Folder sebelum Jackson mengatasinya secara dalaman.
Mari ubah suai serializer kami dan tambahkan medan tambahan - defaultSerializer :
private final JsonSerializer defaultSerializer; public FolderJsonSerializer(JsonSerializer defaultSerializer) { super(Folder.class); this.defaultSerializer = defaultSerializer; }
Seterusnya, kami akan membuat implementasi BeanSerializerModifier untuk meneruskan serializer lalai:
public class FolderBeanSerializerModifier extends BeanSerializerModifier { @Override public JsonSerializer modifySerializer( SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) { if (beanDesc.getBeanClass().equals(Folder.class)) { return new FolderJsonSerializer((JsonSerializer) serializer); } return serializer; } }
Sekarang, kita perlu mendaftarkan BeanSerializerModifier kami sebagai modul untuk membuatnya berfungsi:
ObjectMapper mapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); module.setSerializerModifier(new FolderBeanSerializerModifier()); mapper.registerModule(module);
Kemudian, kami menggunakan defaultSerializer untuk medan perincian :
@Override public void serialize(Folder value, JsonGenerator gen, SerializerProvider provider) throws IOException { gen.writeStartObject(); gen.writeStringField("name", value.getName()); provider.defaultSerializeField("files", value.getFiles(), gen); gen.writeFieldName("details"); defaultSerializer.serialize(value, gen, provider); gen.writeEndObject(); }
Terakhir, kami mungkin ingin membuang bidang fail dari perincian kerana kami sudah menuliskannya ke dalam data bersiri secara berasingan.
Oleh itu, kami hanya mengabaikan bidang fail di kelas Folder kami :
@JsonIgnore private List files = new ArrayList();
Akhirnya, masalah itu diselesaikan dan kami juga mendapat hasil yang diharapkan:
{ "name": "Root Folder", "files": [ {"id": 1, "name": "File 1"}, {"id": 2, "name": "File 2"} ], "details": { "id":1, "name": "Root Folder", "owner": "root", "created": 1565203657164, "modified": 1565203657164, "lastAccess": 1565203657164 } }
6. Kesimpulannya
Dalam tutorial ini, kami belajar bagaimana memanggil serializer lalai di dalam serializer khusus di Jackson Library.
Seperti biasa, semua contoh kod yang digunakan dalam tutorial ini terdapat di GitHub.