Kecualikan Medan dari Serialisasi di Gson

1. Gambaran keseluruhan

Dalam tutorial ringkas ini, kita akan meneroka pilihan yang tersedia untuk mengecualikan satu atau lebih bidang dari kelas Java dan subkelasnya dari serialisasi Gson.

2. Persediaan Awal

Mari tentukan kelas kami terlebih dahulu:

@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 memberi penjelasan kepada mereka dengan Lombok untuk kemudahan (gula sintaksis untuk pengambil, pengatur, pembina…).

Sekarang mari kita isi:

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

Matlamat kami adalah untuk mengelakkan medan MyClass.other dan MySubClass.otherVerboseInfo daripada bersiri.

Keluaran 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 Sementara

Kita boleh menandakan medan dengan pengubah sementara :

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; } 

Penyusun siri Gson akan mengabaikan setiap bidang yang dinyatakan sebagai sementara:

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

Walaupun ini sangat cepat, ia juga dilengkapi dengan kelemahan yang teruk: setiap alat siri akan mengambil kira sementara , bukan hanya Gson.

Fana cara Java untuk mengecualikan dari serialization, maka bidang kami juga akan ditapis oleh Serializable serialization 's, dan dengan setiap alat perpustakaan atau menguruskan objek kami rangka kerja.

Selain itu, kata kunci sementara selalu berfungsi untuk kedua-dua siri dan deserialisasi, yang boleh menjadi terhad bergantung pada kes penggunaan.

4. @Expose Anotasi

Gson com.google.gson.annotations @Expose anotation berfungsi sebaliknya.

Kita boleh menggunakannya untuk menyatakan bidang mana yang akan bersiri, 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 mewujudkan 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 penyaringan harus dilakukan untuk serialisasi, deserialization, atau keduanya (default).

Mari lihat bagaimana untuk mencegah MyClass.other daripada bersiri, tetapi membiarkannya diisi semasa deserialization dari JSON:

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

Walaupun ini adalah cara termudah yang disediakan oleh Gson, dan ini tidak mempengaruhi perpustakaan lain, ini boleh menyiratkan kelebihan dalam kod. Sekiranya kita mempunyai kelas dengan seratus bidang, dan kita hanya mahu mengecualikan satu bidang, kita perlu menulis sembilan puluh sembilan anotasi, yang berlebihan.

5. Strategi Pengecualian

Penyelesaian yang sangat disesuaikan adalah penggunaan com.google.gson. Strategi Pengecualian .

Ini memungkinkan kita untuk menentukan (secara luaran atau dengan Kelas Dalam Anonimous) strategi untuk menginstruksikan GsonBuilder sama ada untuk membuat siri bidang (dan / atau kelas) dengan kriteria tersuai.

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

Mari lihat beberapa contoh strategi pintar untuk digunakan.

5.1. Dengan Nama Kelas dan Padang

Sudah tentu, kita juga boleh menuliskan satu atau lebih nama bidang / 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 tepat, tetapi tidak boleh digunakan semula dan juga terdedah kepada kesilapan sekiranya kita mengubah nama atribut kita.

5.2. Dengan Kriteria Perniagaan

Oleh kerana kita hanya perlu mengembalikan boolean, kita dapat menerapkan setiap logik perniagaan yang kita suka dalam kaedah itu.

Dalam contoh berikut, kami akan mengenal pasti setiap bidang yang dimulakan dengan "lain" sebagai bidang yang tidak seharusnya bersiri, tidak kira 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 Tersuai

Pendekatan pintar lain adalah membuat anotasi tersuai:

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

Kami kemudian dapat memanfaatkan ExclusionStrategy untuk membuatnya berfungsi sama 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; } 

Inilah strategi:

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

Jawapan StackOverflow ini pertama kali menerangkan teknik ini.

Ini membolehkan kita menulis anotasi dan Strategi sekali, dan secara dinamis memberi anotasi bidang kita tanpa pengubahsuaian lebih lanjut.

5.4. Luaskan Strategi Pengecualian ke Deserialisasi

Tidak kira strategi mana yang akan kita gunakan, kita selalu dapat mengawal di mana strategi itu harus diterapkan.

Hanya semasa bersiri:

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

Hanya semasa deserialisasi:

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

Sentiasa:

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

6. Kesimpulannya

Kami telah melihat pelbagai cara untuk mengecualikan bidang dari kelas dan subkelasnya semasa penggambaran Gson.

Kami juga telah meneroka kelebihan dan perangkap utama setiap penyelesaian.

Seperti biasa, kod sumber penuh tersedia di Github.