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

Jackson,Spring
Remote
2
07:27 PM · Nov 30 ,2025

1. 概述

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

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

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

2. 空值

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

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

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

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

如果任何字段為 null, 並且我們只在討論引用字段時,它們將不會包含在生成的 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. 缺失值

Optional實際上是一個non-null值。但是,在請求或響應中傳遞表示不存在值的包裝器在技術上沒有意義。之前的註解無法處理這種情況,並且會嘗試添加有關包裝器本身的某些信息:


{
  "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值。Optionals的最初目的。為了忽略空Optional值,我們可以使用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);
}

Optional值和nulls,以便我們可以用於這兩個場景。

4. 空值

我們是否應該在生成的JSON中包含空字符串或空集合?在大多數情況下,這沒有意義。將它們設置為null或用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);
}

正如之前提到的,所有這些註解都可以更精細地使用,我們甚至可以為不同的字段應用不同的策略。此外,我們可以全局配置我們的mapper來將此規則應用於任何轉換。

5. 自定義映射器

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

public class CustomEmployeeSerializer extends StdSerializer<Employee> {
    @Override
    public void serialize(Employee employee, JsonGenerator gen, SerializerProvider provider)
      throws IOException {
        gen.writeStartObject();
        // 自定義邏輯來序列化其他字段
        gen.writeEndObject();
    }
}

6. 結論

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

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

發佈 評論

Some HTML is okay.