知識庫 / JSON / Jackson RSS 訂閱

對象反序列化後驗證

Jackson
HongKong
6
09:48 PM · Dec 05 ,2025

1. 概述

在本教程中,我們將學習如何使用 Java 的 Validation API 在反序列化後驗證對象。

2. 手動觸發驗證

Java 的 Bean 驗證 API 定義在 JSR 380 中。它的一般用法是在 Spring 控制器中使用的帶有 <em >@Valid</em> 註解的參數。然而,在本文中,我們將重點關注在控制器之外的驗證。

首先,我們編寫一個方法來驗證對象的內容是否符合其驗證約束。為此,我們將從默認驗證器工廠中獲取 `Validator`。然後,我們將將 `validate()` 方法應用於對象。此方法返回一個 `Set`,其中包含 `ConstraintViolation`。`ConstraintViolation` 封裝了有關驗證錯誤的提示。為了保持簡單,如果發生任何驗證問題,我們將拋出 `ConstraintViolationException`:

<T> void validate(T t) {
    Set<ConstraintViolation<T>> violations = validator.validate(t);
    if (!violations.isEmpty()) {
        throw new ConstraintViolationException(violations);
    }
}

如果我們在對象上調用此方法,如果該對象不遵守任何驗證約束,則會拋出異常。此方法可以在附加了約束的現有對象上隨時調用。

3. 將驗證過程融入反序列化流程

我們的目標是現在將驗證過程融入反序列化流程。具體來説,我們將覆蓋 Jackson 的反序列化器,在執行反序列化後立即進行驗證。 這樣可以確保每次反序列化對象時,如果對象不符合要求,則不會進行任何進一步的處理。

首先,我們需要覆蓋默認的 BeanDeserializerBeanDeserializer 是一個可以反序列化對象的類。 我們將調用基類反序列化方法,然後將 validate() 方法應用於創建的實例。 我們的 BeanDeserializerWithValidation 如下所示:

public class BeanDeserializerWithValidation extends BeanDeserializer {

    protected BeanDeserializerWithValidation(BeanDeserializerBase src) {
        super(src);
    }

    @Override
    public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException {
        Object instance = super.deserialize(p, ctxt);
        validate(instance);
        return instance;
    }

}

下一步是實現我們自己的 BeanDeserializerModifier。這將允許我們通過 BeanDeserializerWithValidation 中定義的行為來修改反序列化過程。

public class BeanDeserializerModifierWithValidation extends BeanDeserializerModifier {

    @Override
    public JsonDeserializer<?> modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer<?> deserializer) {
        if (deserializer instanceof BeanDeserializer) {
            return new BeanDeserializerWithValidation((BeanDeserializer) deserializer);
        }

        return deserializer;
    }

}

最後,我們需要創建一個 ObjectMapper 並將我們的 BeanDeserializerModifier 註冊為一個 ModuleModule 是一種擴展 Jackson 默認功能的途徑。 讓我們將其封裝在一個方法中:

ObjectMapper getObjectMapperWithValidation() {
    SimpleModule validationModule = new SimpleModule();
    validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation());
    ObjectMapper mapper = new ObjectMapper();
    mapper.registerModule(validationModule);
    return mapper;
}

4. 示例用法:從文件讀取和驗證對象

我們將展示如何使用我們的自定義 <em >ObjectMapper</em> 的一個小示例。首先,讓我們定義一個 <em >Student</em> 對象。一個 <em >Student</em> 對象具有一個姓名。該姓名長度必須在 5 到 10 個字符之間:

public class Student {

    @Size(min = 5, max = 10, message = "Student's name must be between 5 and 10 characters")
    private String name;

    public String getName() {
        return name;
    }

}

現在,讓我們創建一個 validStudent.json 文件,其中包含一個有效 Student 對象的 JSON 表示形式:

{
  "name": "Daniel"
}

我們將會讀取該文件的內容,通過 InputStream。首先,讓我們定義一個方法,該方法將 InputStream 解析為 Student 對象,並在同一時間進行驗證。為此,我們將使用我們的 ObjectMapper

Student readStudent(InputStream inputStream) throws IOException {
    ObjectMapper mapper = getObjectMapperWithValidation();
    return mapper.readValue(inputStream, Student.class);
}

我們現在可以編寫一個測試,其中我們將:

  • 首先,將文件的內容讀取到一個 InputStream
  • InputStream 轉換為一個 Student 對象
  • 檢查 Student 對象的有效性是否與預期一致

這個測試如下所示:

@Test
void givenValidStudent_WhenReadStudent_ThenReturnStudent() throws IOException {
    InputStream inputStream = getClass().getClassLoader().getResourceAsStream(("validStudent.json");
    Student result = readStudent(inputStream);
    assertEquals("Daniel", result.getName());
}

同樣,我們還可以創建一個 invalid.json 文件,其中包含一個 Student 的 JSON 表示形式,該 Student 的名字長度小於 5 個字符:

{
  "name": "Max"
}

現在我們需要調整測試用例,以確保當違反約束條件時,確實會拋出 ConstraintViolationException 異常。 此外,我們還可以檢查錯誤消息是否正確:

@Test
void givenStudentWithInvalidName_WhenReadStudent_ThenThrows() {
    InputStream inputStream = getClass().getClassLoader().getResourceAsStream("invalidStudent.json");
    ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> readStudent(inputStream));
    assertEquals("name: Student's name must be between 5 and 10 characters", constraintViolationException.getMessage());
}

5. 結論

在本文中,我們瞭解到如何覆蓋 Jackson 的配置以在反序列化對象後立即對其進行驗證。 這樣可以確保無法在之後處理無效對象。

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

發佈 評論

Some HTML is okay.