知識庫 / Spring RSS 訂閱

移除 JSON 響應中的空對象,使用 Spring 和 Jackson

Jackson,Spring
HongKong
14
09:44 PM · Dec 05 ,2025

1. 概述

JSON 是一種事實上的標準,用於 RESTful 應用程序。Spring 使用 Jackson 庫無縫地將對象轉換為 JSON 格式,反之亦然。然而,有時我們希望自定義轉換並提供特定的規則。

例如,忽略響應或請求中的空值或空值。 這可能會帶來性能優勢,因為我們不需要發送空值來回。 此外,這也有助於使我們的 API 更簡潔。

在本教程中,我們將學習如何利用 Jackson 映射來簡化我們的 REST 交互。

2. 空值

在發送或接收請求時,我們經常會看到值被設置為 nulls. 然而,通常情況下,這並不能為我們提供任何有用的信息,因為在大多數情況下,這只是未定義變量或字段的默認值。

此外,我們允許將 null 值通過 JSON 傳遞,這會使驗證過程變得複雜。我們可以跳過驗證並將其設置為默認值,如果值不存在。但是,如果值存在,則需要進行額外的檢查以確定它是否為 null 並且是否可以將其轉換為一些合理的表示形式。

Jackson 提供了在我們的類中直接配置它的便捷方式。 我們將使用 Include.NON_NULL. 它可以用於類級別,如果規則適用於所有字段,或者我們可以更精細地在字段、getter 和 setter 上使用它。 讓我們考慮以下 Employee 類:

@JsonInclude(Include.NON_NULL)
public class Employee {
    private String lastName;
    private String firstName;
    private long id;
    // constructors, getters and setters
}

如果任何字段為 null</em/>,且僅討論引用字段,則不會包含在生成的JSON中:

@ParameterizedTest
@MethodSource
void giveEndpointWhenSendEmployeeThanReceiveThatUserBackIgnoringNullValues(Employee expected) throws Exception {
    MvcResult result = sendRequestAndGetResult(expected, USERS);
    String response = result.getResponse().getContentAsString();
    validateJsonFields(expected, response);
}

private void validateJsonFields(Employee expected, String response) throws JsonProcessingException {
    JsonNode jsonNode = mapper.readTree(response);
    Predicate<Field> nullField = s -> isFieldNull(expected, s);
    List<String> nullFields = filterFieldsAndGetNames(expected, nullField);
    List<String> nonNullFields = filterFieldsAndGetNames(expected, nullField.negate());
    nullFieldsShouldBeMissing(nullFields, jsonNode);
    nonNullFieldsShouldNonBeMissing(nonNullFields, jsonNode);
}

有時,我們希望複製對 null 類似字段的行為,Jackson 也提供了處理這些字段的方法。

3. 缺失值

空值(Empty)實際上是一個可選對象,從技術上講,它是一個非空值。但是,在請求或響應中傳遞表示不存在值的包裝器,在實踐中沒有意義。之前的註解無法處理這種情況,並且會嘗試添加關於包裝器本身的某些信息:

{
  "lastName": "John",
  "firstName": "Doe",
  "id": 1,
  "salary": {
    "empty": true,
    "present": false
  }
}

讓我們設想一下,如果我們的公司中的每位員工都願意公開他們的薪資,那麼會怎樣:

@JsonInclude(Include.NON_ABSENT)
public class Employee {
    private String lastName;
    private String firstName;
    private long id;
    private Optional<Salary> salary;
    // constructors, getters and setters
}

我們可以使用自定義的 Getter 和 Setter 方法來返回 null 值。 然而,這會使 API 複雜化,並且會忽略使用 Optionals 的初衷。 為了忽略空 Optionals,我們可以使用 Include.NON_ABSENT

private void validateJsonFields(Employee expected, String response) throws JsonProcessingException {
    JsonNode jsonNode = mapper.readTree(response);
    Predicate<Field> nullField = s -> isFieldNull(expected, s);
    Predicate<Field> absentField = s -> isFieldAbsent(expected, s);
    List<String> nullOrAbsentFields = filterFieldsAndGetNames(expected, nullField.or(absentField));
    List<String> nonNullAndNonAbsentFields = filterFieldsAndGetNames(expected, nullField.negate().and(absentField.negate()));
    nullFieldsShouldBeMissing(nullOrAbsentFields, jsonNode);
    nonNullFieldsShouldNonBeMissing(nonNullAndNonAbsentFields, jsonNode);
}

Include.NON_ABSENT 處理空 Optional 值和 nulls,以便我們能夠在兩種情況下使用它。

4. 空值

我們是否應該在生成的 JSON 中包含空字符串或空集合? 在大多數情況下,這沒有意義。 將它們設置為 nulls 或用 Optionals 包裹也可能不是一個好主意,並且可能會使與對象之間的交互變得複雜。

讓我們考慮一些關於我們員工的額外信息。 鑑於我們公司在國際化環境中運作,員工可能希望添加他們名字的音譯版本,並且他們可能提供電話號碼或電話號碼以便他人聯繫他們。

@JsonInclude(Include.NON_EMPTY)
public class Employee {
    private String lastName;
    private String firstName;
    private long id;
    private Optional<Salary> salary;
    private String phoneticName = "";
    private List<PhoneNumber> phoneNumbers = new ArrayList<>();
    // constructors, getters and setters
}

我們可以使用 Include.NON_EMPTY 來排除空值。 此配置還忽略了 null 和缺失值:

private void validateJsonFields(Employee expected, String response) throws JsonProcessingException {
    JsonNode jsonNode = mapper.readTree(response);
    Predicate<Field> nullField = s -> isFieldNull(expected, s);
    Predicate<Field> absentField = s -> isFieldAbsent(expected, s);
    Predicate<Field> emptyField = s -> isFieldEmpty(expected, s);
    List<String> nullOrAbsentOrEmptyFields = filterFieldsAndGetNames(expected, nullField.or(absentField).or(emptyField));
    List<String> nonNullAndNonAbsentAndNonEmptyFields = filterFieldsAndGetNames(expected,
      nullField.negate().and(absentField.negate().and(emptyField.negate())));
    nullFieldsShouldBeMissing(nullOrAbsentOrEmptyFields, jsonNode);
    nonNullFieldsShouldNonBeMissing(nonNullAndNonAbsentAndNonEmptyFields, jsonNode);
}

正如之前所述,這些註釋可以更精細地使用,並且我們甚至可以針對不同的字段應用不同的策略。此外,我們還可以全局配置映射器,以將該規則應用於任何轉換。

5. 自定義映射器

如果上述策略無法滿足我們的需求或需要支持特定的約定,我們應該使用 <em Include.CUSTOM</em> 或實現自定義序列化器:

public class CustomEmployeeSerializer extends StdSerializer<Employee> {
    @Override
    public void serialize(Employee employee, JsonGenerator gen, SerializerProvider provider)
      throws IOException {
        gen.writeStartObject();
        // Custom logic to serialize other fields
        gen.writeEndObject();
    }
}

6. 結論

Jackson 和 Spring 可以幫助我們開發具有最小配置需求的 RESTful 應用。 採用合適的集成策略可以簡化我們的 API 並減少冗餘代碼量。 同時,如果默認解決方案過於嚴格或不靈活,我們可以通過自定義映射器或過濾器進行擴展。

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

發佈 評論

Some HTML is okay.