Kecualikan Fields dari Serialization di Gson

1. Ikhtisar

Dalam tutorial singkat ini, kita akan menjelajahi opsi yang tersedia untuk mengecualikan satu atau beberapa bidang kelas Java dan subkelasnya dari serialisasi Gson.

2. Pengaturan Awal

Pertama mari kita tentukan kelas kita:

@Data @AllArgsConstructor public class MyClass { private long id; private String name; private String other; private MySubClass subclass; } @Data @AllArgsConstructor public class MySubClass { private long id; private String description; private String otherVerboseInfo; } 

Kami telah menjelaskannya dengan Lombok untuk kenyamanan (gula sintaksis untuk pengambil, penyetel, konstruktor…).

Sekarang mari kita isi mereka:

MySubClass subclass = new MySubClass(42L, "the answer", "Verbose field not to serialize") MyClass source = new MyClass(1L, "foo", "bar", subclass); 

Tujuan kami adalah untuk mencegah MyClass.other dan MySubClass.otherVerboseInfo bidang dari yang serial.

Output yang kami harapkan adalah:

{ "id":1, "name":"foo", "subclass":{ "id":42, "description":"the answer" } } 

Di Jawa:

String expectedResult = "{\"id\":1,\"name\":\"foo\",\"subclass\":{\"id\":42,\"description\":\"the answer\"}}"; 

3. Pengubah Transien

Kita bisa menandai sebuah field dengan transient modifier:

public class MyClass { private long id; private String name; private transient String other; private MySubClass subclass; } public class MySubClass { private long id; private String description; private transient String otherVerboseInfo; } 

Gson serializer akan mengabaikan setiap bidang yang dideklarasikan sebagai sementara:

String jsonString = new Gson().toJson(source); assertEquals(expectedResult, jsonString); 

Meskipun ini sangat cepat, ini juga memiliki kerugian yang parah: setiap alat serialisasi akan memperhitungkan sementara , tidak hanya Gson.

Transient adalah cara Java untuk mengecualikan dari serialisasi, kemudian bidang kami juga akan disaring oleh serialisasi Serializable, dan oleh setiap alat perpustakaan atau kerangka kerja yang mengelola objek kami.

Selain itu, kata kunci transien selalu berfungsi untuk serialisasi dan deserialisasi, yang dapat membatasi tergantung pada kasus penggunaan.

4. @Expose Annotation

Gson com.google.gson.annotations @Expose penjelasan bekerja sebaliknya.

Kita bisa menggunakannya untuk mendeklarasikan field mana yang akan dibuat serial, dan mengabaikan yang lain:

public class MyClass { @Expose private long id; @Expose private String name; private String other; @Expose private MySubClass subclass; } public class MySubClass { @Expose private long id; @Expose private String description; private String otherVerboseInfo; } 

Untuk ini, kita perlu membuat instance Gson dengan GsonBuilder:

Gson gson = new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() .create(); String jsonString = gson.toJson(source); assertEquals(expectedResult, jsonString); 

Kali ini kita dapat mengontrol di tingkat lapangan apakah pemfilteran harus dilakukan untuk serialisasi, deserialisasi, atau keduanya (default).

Mari kita lihat cara mencegah MyClass.other agar tidak diserialisasi, tetapi mengizinkannya untuk diisi selama deserialisasi dari JSON:

@Expose(serialize = false, deserialize = true) private String other; 

Meskipun ini adalah cara termudah yang disediakan oleh Gson, dan tidak memengaruhi pustaka lain, ini dapat menyiratkan redundansi dalam kode. Jika kita memiliki kelas dengan ratusan bidang, dan kita hanya ingin mengecualikan satu bidang, kita perlu menulis sembilan puluh sembilan anotasi, yang berlebihan.

5. Strategi Pengecualian

Solusi yang sangat dapat disesuaikan adalah penggunaan com.google.gson. ExclusionStrategy .

Ini memungkinkan kita untuk menentukan (secara eksternal atau dengan Kelas Dalam Anonimous) strategi untuk menginstruksikan GsonBuilder apakah akan membuat serial bidang (dan / atau kelas) dengan kriteria khusus.

Gson gson = new GsonBuilder() .addSerializationExclusionStrategy(strategy) .create(); String jsonString = gson.toJson(source); assertEquals(expectedResult, jsonString); 

Mari kita lihat beberapa contoh strategi cerdas untuk digunakan.

5.1. Dengan Kelas dan Nama Bidang

Tentu saja, kami juga dapat membuat kode keras satu atau lebih bidang / nama kelas:

ExclusionStrategy strategy = new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes field) { if (field.getDeclaringClass() == MyClass.class && field.getName().equals("other")) { return true; } if (field.getDeclaringClass() == MySubClass.class && field.getName().equals("otherVerboseInfo")) { return true; } return false; } @Override public boolean shouldSkipClass(Class clazz) { return false; } }; 

Ini cepat dan langsung ke intinya, tetapi tidak terlalu dapat digunakan kembali dan juga rentan terhadap kesalahan jika kami mengganti nama atribut kami.

5.2. Dengan Kriteria Bisnis

Karena kita hanya perlu mengembalikan boolean, kita dapat mengimplementasikan setiap logika bisnis yang kita suka di dalam metode itu.

Dalam contoh berikut, kami akan mengidentifikasi setiap bidang yang dimulai dengan "lainnya" sebagai bidang yang tidak boleh diserialkan, apa pun kelasnya:

ExclusionStrategy strategy = new ExclusionStrategy() { @Override public boolean shouldSkipClass(Class clazz) { return false; } @Override public boolean shouldSkipField(FieldAttributes field) { return field.getName().startsWith("other"); } }; 

5.3. Dengan Anotasi Kustom

Pendekatan cerdas lainnya adalah membuat anotasi khusus:

@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface Exclude {} 

Kemudian kita dapat mengeksploitasi ExclusionStrategy untuk membuatnya bekerja persis seperti dengan anotasi @Expose , tetapi sebaliknya:

public class MyClass { private long id; private String name; @Exclude private String other; private MySubClass subclass; } public class MySubClass { private long id; private String description; @Exclude private String otherVerboseInfo; } 

Dan inilah strateginya:

ExclusionStrategy strategy = new ExclusionStrategy() { @Override public boolean shouldSkipClass(Class clazz) { return false; } @Override public boolean shouldSkipField(FieldAttributes field) { return field.getAnnotation(Exclude.class) != null; } }; 

Jawaban StackOverflow ini pertama kali menjelaskan teknik ini.

Ini memungkinkan kita untuk menulis anotasi dan Strategi sekali, dan secara dinamis menganotasi bidang kita tanpa modifikasi lebih lanjut.

5.4. Perluas Strategi Pengecualian ke Deserialisasi

Tidak peduli strategi mana yang akan kami gunakan, kami selalu dapat mengontrol di mana strategi itu harus diterapkan.

Hanya selama serialisasi:

Gson gson = new GsonBuilder().addSerializationExclusionStrategy(strategy) 

Hanya selama deserialization:

Gson gson = new GsonBuilder().addDeserializationExclusionStrategy(strategy) 

Selalu:

Gson gson = new GsonBuilder().setExclusionStrategies(strategy); 

6. Kesimpulan

Kami telah melihat berbagai cara untuk mengecualikan bidang dari kelas dan subkelasnya selama serialisasi Gson.

Kami juga telah menjelajahi keuntungan dan kelemahan utama dari setiap solusi.

Seperti biasa, kode sumber lengkap tersedia di Github.