1. 概述
本教程重點介紹 Jackson 的 ObjectMapper 類及其如何將 Java 對象序列化為 JSON 以及如何將 JSON 字符串反序列化為 Java 對象。
要了解 Jackson 庫的更多信息,Jackson 教程是一個不錯的起點。
2. 依賴項
以下依賴項請添加到 `pom.xml</em/> 中:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.17.2</version>
</dependency>
這個依賴項也會間接添加以下庫到 classpath 中:
- jackson-annotations
- jackson-core
請始終從 Maven 中央倉庫獲取最新版本 jackson-databind。
3. 使用 ObjectMapper 進行讀取和寫入操作
讓我們從基本的讀取和寫入操作開始。
簡單的 readValue API 在 ObjectMapper 中是一個不錯的入門點。 我們可以使用它來解析或反序列化 JSON 內容到 Java 對象。
在寫入方面,我們可以使用 writeValue API 將任何 Java 對象序列化為 JSON 輸出。
在本文中,我們將使用以下 Car 類,該類具有兩個字段,作為要序列化或反序列化的對象:
public class Car {
private String color;
private String type;
// standard getters setters
}3.1. Java 對象到 JSON
讓我們來看一個使用 writeValue 方法將 Java 對象序列化為 JSON 的第一個示例,該方法位於 ObjectMapper 類中:
ObjectMapper objectMapper = new ObjectMapper();
Car car = new Car("yellow", "renault");
objectMapper.writeValue(new File("target/car.json"), car);
以上文件的輸出將是:
{"color":"yellow","type":"renault"}
writeValueAsString 和 writeValueAsBytes 是 ObjectMapper 類中的兩個方法,它們分別生成 JSON 字符串並返回,或生成 JSON 字節數組:
String carAsString = objectMapper.writeValueAsString(car);
3.2. JSON 到 Java 對象
以下是一個使用 ObjectMapper 類將 JSON 字符串轉換為 Java 對象的簡單示例:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Car car = objectMapper.readValue(json, Car.class);
readValue() 函數也接受其他形式的輸入,例如包含 JSON 字符串的文件:
Car car = objectMapper.readValue(new File("src/test/resources/json_car.json"), Car.class);或一個 URL:
Car car =
objectMapper.readValue(new URL("file:src/test/resources/json_car.json"), Car.class);3.3. JSON 到 Jackson 的 JsonNode
也可以將 JSON 解析為 JsonNode 對象,並從中檢索特定節點的數據:
String json = "{ \"color\" : \"Black\", \"type\" : \"FIAT\" }";
JsonNode jsonNode = objectMapper.readTree(json);
String color = jsonNode.get("color").asText();
// Output: color -> Black
3.4. 從 JSON 數組字符串創建 Java List
我們可以使用 TypeReference 解析 JSON 格式的數組,將其轉換為 Java 對象列表。
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});
3.5. 從 JSON 字符串創建 Java Map
類似於地,我們可以將 JSON 解析為 Java 的 Map:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
Map<String, Object> map
= objectMapper.readValue(json, new TypeReference<Map<String,Object>>(){});
4. 高級功能
Jackson 庫的最大優勢之一是高度可定製的序列化和反序列化過程。
在這一部分,我們將探討一些高級功能,其中輸入或輸出 JSON 響應可以與生成或消耗響應的對象不同。
<h3><strong>4.1. 配置序列化或反序列化功能</strong></h3>
<p>在將 JSON 對象轉換為 Java 類時,如果 JSON 字符串包含新的字段,則默認過程將導致異常。</p>
String jsonString
= "{ \"color\" : \"Black\", \"type\" : \"Fiat\", \"year\" : \"1970\" }";
在上述示例中,將 JSON 字符串傳遞給 Java 對象 Class Car 進行默認解析,會導致 UnrecognizedPropertyException 異常。
通過 configure 方法,我們可以擴展默認流程,忽略新的字段。
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Car car = objectMapper.readValue(jsonString, Car.class);
JsonNode jsonNodeRoot = objectMapper.readTree(jsonString);
JsonNode jsonNodeYear = jsonNodeRoot.get("year");
String year = jsonNodeYear.asText();
另一個選項基於 FAIL_ON_NULL_FOR_PRIMITIVES,它定義了是否允許對原始類型的值使用 null 值:
objectMapper.configure(DeserializationFeature.FAIL_ON_NULL_FOR_PRIMITIVES, false);
同樣,<em>FAIL_ON_NUMBERS_FOR_ENUM</em> 控制枚舉值是否允許被序列化/反序列化為數字:
objectMapper.configure(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS, false);你可以找到序列化和反序列化的完整列表,請訪問官方網站。
4.2. 創建自定義序列化器或反序列器
ObjectMapper 類的一個關鍵功能是能夠註冊自定義序列化器和反序列器。
自定義序列化器和反序列器在輸入或輸出 JSON 響應與必須序列化或反序列化到 Java 類所具有的結構不同時非常有用。
以下是一個自定義 JSON 序列化器的示例:
public class CustomCarSerializer extends StdSerializer<Car> {
public CustomCarSerializer() {
this(null);
}
public CustomCarSerializer(Class<Car> t) {
super(t);
}
@Override
public void serialize(
Car car, JsonGenerator jsonGenerator, SerializerProvider serializer) {
jsonGenerator.writeStartObject();
jsonGenerator.writeStringField("car_brand", car.getType());
jsonGenerator.writeEndObject();
}
}
此自定義序列化器可以這樣調用:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module =
new SimpleModule("CustomCarSerializer", new Version(1, 0, 0, null, null, null));
module.addSerializer(Car.class, new CustomCarSerializer());
mapper.registerModule(module);
Car car = new Car("yellow", "renault");
String carJson = mapper.writeValueAsString(car);
以下是翻譯後的內容:
以下是客户端的 汽車 (以 JSON 格式輸出) 所示:
var carJson = {"car_brand":"renault"}
以下是一個自定義 JSON 反序列化器示例:
public class CustomCarDeserializer extends StdDeserializer<Car> {
public CustomCarDeserializer() {
this(null);
}
public CustomCarDeserializer(Class<?> vc) {
super(vc);
}
@Override
public Car deserialize(JsonParser parser, DeserializationContext deserializer) {
Car car = new Car();
ObjectCodec codec = parser.getCodec();
JsonNode node = codec.readTree(parser);
// try catch block
JsonNode colorNode = node.get("color");
String color = colorNode.asText();
car.setColor(color);
return car;
}
}
可以使用以下方式調用此自定義反序列器:
String json = "{ \"color\" : \"Black\", \"type\" : \"BMW\" }";
ObjectMapper mapper = new ObjectMapper();
SimpleModule module =
new SimpleModule("CustomCarDeserializer", new Version(1, 0, 0, null, null, null));
module.addDeserializer(Car.class, new CustomCarDeserializer());
mapper.registerModule(module);
Car car = mapper.readValue(json, Car.class);
4.3. 處理日期格式
默認情況下,java.util.Date 的序列化會產生一個數字,即從協調世界時(UTC)1970 年 1 月 1 日以來的毫秒數(時間戳)。但這種格式不太易於人類閲讀,需要進行進一步轉換才能以人類可讀的格式顯示。
讓我們將我們之前使用的 Car 實例包裹在 Request 類中,並添加 datePurchased 屬性:
public class Request
{
private Car car;
private Date datePurchased;
// standard getters setters
}
為了控制日期的格式並將其設置為,例如 yyyy-MM-dd HH:mm a z,請考慮以下代碼片段:
ObjectMapper objectMapper = new ObjectMapper();
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
objectMapper.setDateFormat(df);
String carAsString = objectMapper.writeValueAsString(request);
// output: {"car":{"color":"yellow","type":"renault"},"datePurchased":"2016-07-03 11:43 AM CEST"}
為了更深入地瞭解使用 Jackson 序列化日期,請閲讀我們的詳細説明文檔。
請注意,在某些情況下,創建 SimpleDateFormat 時,我們需要明確指定 Locale,以便在不受地域影響的機器上獲得一致的輸出。
要指定 Locale,可以這樣做:
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm a z", Locale.ENGLISH);4.4. 處理集合 (Handling Collections)
另一個通過 <em >DeserializationFeature</em> 類提供的實用功能是,能夠從 JSON Array 響應中生成我們所需的集合類型。
例如,我們可以將結果生成為數組:
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
Car[] cars = objectMapper.readValue(jsonCarArray, Car[].class);
// print cars或者,以列表形式:
String jsonCarArray =
"[{ \"color\" : \"Black\", \"type\" : \"BMW\" }, { \"color\" : \"Red\", \"type\" : \"FIAT\" }]";
ObjectMapper objectMapper = new ObjectMapper();
List<Car> listCar = objectMapper.readValue(jsonCarArray, new TypeReference<List<Car>>(){});
// print cars有關使用 Jackson 處理集合的更多信息,請參閲這裏。
5. 使用 Builder 模式為 ObjectMapper 構建
到目前為止,我們已經學習了配置 ObjectMapper 實例的不同方法。 在本節中,我們將創建一個 ObjectMapperBuilder 類,以創建 ObjectMapper 類的不可變實例。
5.1. ObjectMapperBuilder 類
讓我們首先創建一個 ObjectMapperBuilder 類,並配置一些參數,包括 enableIdentation、preserveOrder 和 dateFormat:
public class ObjectMapperBuilder {
private boolean enableIndentation;
private boolean preserveOrder;
private DateFormat dateFormat;
}必須注意的是,ObjectMapper 實例有多種可能配置。我們目前僅關注為原型構建用例選擇的一組配置子集。
接下來,讓我們添加方法,以便在創建 ObjectMapper 類實例時,將相應的配置屬性設置為構建器。
ObjectMapperBuilder enableIndentation() {
this.enableIndentation = true;
return this;
}
ObjectMapperBuilder dateFormat() {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm a z");
simpleDateFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("Asia/Kolkata")));
this.dateFormat = simpleDateFormat;
return this;
}
ObjectMapperBuilder preserveOrder(boolean order) {
this.preserveOrder = order;
return this;
}最後,讓我們添加build()方法,以返回帶有配置參數的最終 ObjectMapper 實例:
public ObjectMapper build() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.INDENT_OUTPUT, this.enableIndentation);
objectMapper.setDateFormat(this.dateFormat);
if (this.preserveOrder) {
objectMapper.enable(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS);
}
return objectMapper;
}太棒了!我們已成功原型化了 ObjectMapper 實例的 builder 類。
5.2. Builder 示例
讓我們通過使用 ObjectMapperBuilder 類,創建一個 ObjectMapper 類的實例:
ObjectMapper mapper = new ObjectMapperBuilder()
.enableIndentation()
.dateFormat()
.preserveOrder(true)
.build();現在,讓我們定義 Car 類及其序列化的 JSON 字符串:
Car givenCar = new Car("White", "Sedan");
String givenCarJsonStr = "{ \"color\" : \"White\", \"type\" : \"Sedan\" }";繼續進行,我們使用 mapper 對象來反序列化 givenCarJsonStr:
Car actual = mapper.readValue(givenCarJsonStr, Car.class);
Assertions.assertEquals("White", actual.getColor());
Assertions.assertEquals("Sedan", actual.getType());完美!看來我們已經搞定了。
最後,讓我們驗證 Request 類的一個實例的序列化流程:
Request request = new Request();
request.setCar(givenCar);
Date date = new Date(1684909857000L);
request.setDatePurchased(date);
String actual = mapper.writeValueAsString(request);
String expected = "{\n" + " \"car\" : {\n" + " \"color\" : \"White\",\n" +
" \"type\" : \"Sedan\"\n" + " },\n" + " \"datePurchased\" : \"2023-05-24 12:00 PM IST\"\n" +
"}";
Assertions.assertEquals(expected, actual);太好了!我們已通過 ObjectMapperBuilder 類成功驗證了序列化和反序列化操作。
6. 結論
Jackson 是一款穩定且成熟的 Java JSON 序列化/反序列化庫。ObjectMapper API 提供了一種簡單直接的方式來解析和生成 JSON 響應對象,具有很大的靈活性。此外,我們對 ObjectMapperBuilder 類進行了原型設計,以創建 ObjectMapper 類的不可變實例。本文討論了使該庫如此流行的主要功能。