知識庫 / JSON RSS 訂閱

Gson 序列化排除字段

Data,JSON
HongKong
9
09:55 PM · Dec 05 ,2025

1. 概述

在本簡短教程中,我們將探討如何從 Gson 序列化中排除 Java 類及其子類的一個或多個字段的可用選項。

2. 初始設置

首先,我們定義我們的類:

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

我們已經用Lombok對其進行了標註,以方便使用(Getter、Setter、構造函數等的語法糖)。

現在我們來填充它們:

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

我們的目標是防止 MyClass.otherMySubClass.otherVerboseInfo 字段被序列化。

我們期望得到的結果是:

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

在Java中:

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

3. 瞬態修飾符

我們可以使用 transient 修飾符標記一個字段:

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序列化器會忽略所有聲明為 transient 的字段。

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

雖然這種方法非常快速,但也伴隨着嚴重的缺點:每一個序列化工具都會考慮 transient,不僅僅是 Gson。

Transient 是 Java 中用來排除從序列化而來的字段的方式,因此我們的字段也會被 Serializable 的序列化以及所有管理我們對象的庫工具或框架過濾。

此外,transient 關鍵字在序列化和反序列化過程中始終有效,這在某些用例中可能會有所限制。

4. @Expose 註解

Gson 的 @Expose 註解是反向工作的。

我們可以使用它來聲明哪些字段需要序列化,而忽略其他的字段:

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

為了實現這一點,我們需要使用 GsonBuilder 實例化 Gson。

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

現在,我們可以控制在序列化、反序列化或兩者(默認)中是否應該進行過濾。

下面是如何阻止 MyClass.other 被序列化,但允許在從 JSON 中進行反序列化時進行填充的方法:

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

雖然Gson提供這種方式是最簡單的方法,並且不會影響其他庫,但它可能會導致代碼冗餘。如果一個類有數百個字段,我們只想排除一個字段,就需要編寫九十九個註解,這顯然是多餘的。

5. 排除策略

使用一個 com.google.gson.ExclusionStrategy 是一種高度可定製的解決方案。

它允許我們定義(通過外部類或匿名內部類)一種策略,用於指導 GsonBuilder 是否應序列化字段(以及/或類),並具有自定義標準。

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

assertEquals(expectedResult, jsonString);

讓我們看看一些智能策略的例子。

5.1. 使用類和字段名稱

當然,我們也可以硬編碼一個或多個字段/類名稱:

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

這速度快且直接,但可重用性不高,並且在重命名我們的屬性時也容易出錯。

5.2. 依據業務規則

由於我們只需要返回一個布爾值,因此我們可以隨意在方法內部實現任何業務邏輯。

在下面的示例中,我們將識別所有以“other”開頭的字段,無論它們所屬的類是什麼,都不應進行序列化:

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. 使用自定義標註

另一種巧妙的方法是創建自定義標註:

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

我們可以利用 ExclusionStrategy 來使其運作方式與 @Expose 註解完全相同,但反過來:

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

以下是策略:

ExclusionStrategy strategy = new ExclusionStrategy() {
    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }

    @Override
    public boolean shouldSkipField(FieldAttributes field) {
        return field.getAnnotation(Exclude.class) != null;
    }
};

這個StackOverflow答案 首先描述了這種技術。

它允許我們一次編寫註解和策略,並在不進行進一步修改的情況下動態地註解我們的字段。

5.4. 將排除策略擴展到反序列化

無論我們使用哪種策略,我們都可以始終控制其應用範圍。

僅在序列化期間:

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

只有在反序列化期間:

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

始終:

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

6. 結論

我們已經看到了在 Gson 序列化過程中從類及其子類中排除字段的不同方法。

我們還探討了每種解決方案的主要優勢和潛在問題。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.