1. 簡介
Jackson 庫在 Java 世界中,尤其是在處理 JSON 方面,被廣泛認為是事實上的標準。儘管 Jackson 具有明確定義的默認設置,但要將 Boolean 值映射到 Integer 時,我們仍然需要進行手動配置。
當然,一些開發者會思考如何以最佳方式和最少的工作量來實現這一點。
在本文中,我們將解釋如何將 Boolean 值序列化為 Integer 值(以及數字字符串)以及反過來,在 Jackson 中實現。
2. 序列化
首先,我們將探討序列化這一部分。為了測試布爾值到整數序列化,我們先定義我們的模型,遊戲:
public class Game {
private Long id;
private String name;
private Boolean paused;
private Boolean over;
// constructors, getters and setters
}通常情況下,Game對象的默認序列化將使用 Jackson 的 ObjectMapper:
ObjectMapper mapper = new ObjectMapper();
Game game = new Game(1L, "My Game");
game.setPaused(true);
game.setOver(false);
String json = mapper.writeValueAsString(game);unsurprisingly, 對於 布爾值 字段的輸出將是默認值——true 或 false。
{"id":1, "name":"My Game", "paused":true, "over":false}然而,我們最終希望從我們的 Game 對象中獲得以下 JSON 輸出:
{"id":1, "name":"My Game", "paused":1, "over":0}2.1. 字段級別配置
將數據序列化為 Integer 的一種簡單方法是,對我們的 Boolean 字段進行註解,並設置 Shape.NUMBER:
@JsonFormat(shape = Shape.NUMBER)
private Boolean paused;
@JsonFormat(shape = Shape.NUMBER)
private Boolean over;然後,我們來在測試方法中嘗試序列化:
ObjectMapper mapper = new ObjectMapper();
Game game = new Game(1L, "My Game");
game.setPaused(true);
game.setOver(false);
String json = mapper.writeValueAsString(game);
assertThat(json)
.isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":1,\"over\":0}");正如我們從JSON輸出中看到的,我們的布爾值字段——暫停和結束——轉換成了數字1和0。由於它們沒有被引號包圍,因此可以清楚地看出這些值是整數格式。
2.2 全局配置
有時,對每個字段進行註釋可能不切實際。例如,根據要求,我們可能需要全局配置我們的 Boolean 為 Integer 序列化。
幸運的是,Jackson 允許我們通過在 ObjectMapper 中覆蓋默認值來全局配置 @JsonFormat:
ObjectMapper mapper = new ObjectMapper();
mapper.configOverride(Boolean.class)
.setFormat(JsonFormat.Value.forShape(Shape.NUMBER));
Game game = new Game(1L, "My Game");
game.setPaused(true);
game.setOver(false);
String json = mapper.writeValueAsString(game);
assertThat(json)
.isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":1,\"over\":0}");3. 反序列化
同樣,在反序列化 JSON 字符串到模型時,我們可能也需要從數字中獲取 布爾值。
幸運的是,Jackson 可以解析數字——僅限於 1 和 0 —— 為 布爾值。因此,我們不需要使用 @JsonFormat 註解或任何其他配置。
因此,無需配置,讓我們通過另一個測試方法來觀察這種行為:
ObjectMapper mapper = new ObjectMapper();
String json = "{\"id\":1,\"name\":\"My Game\",\"paused\":1,\"over\":0}";
Game game = mapper.readValue(json, Game.class);
assertThat(game.isPaused()).isEqualTo(true);
assertThat(game.isOver()).isEqualTo(false);因此,Jackson 默認支持 Integer 到 Boolean 的序列化/反序列化。
4. 使用數字字符串代替整數
另一個使用場景是使用數字字符串——“1” 和 “0”——代替整數。在這種情況下,將 Boolean 值序列化為數字字符串或反序列化為 Boolean 值需要付出更多的努力。
4.1. 將值序列化為數字字符串
為了將 Boolean 值序列化為數字字符串等效值,我們需要定義一個自定義序列化器。
因此,讓我們通過擴展 Jackson 的 JsonSerializer 創建我們的 NumericBooleanSerializer:
public class NumericBooleanSerializer extends JsonSerializer<Boolean> {
@Override
public void serialize(Boolean value, JsonGenerator gen, SerializerProvider serializers)
throws IOException {
gen.writeString(value ? "1" : "0");
}
}順便説明的是,通常情況下,布爾型可以為null。但是,Jackson內部處理了這種情況,並且在值字段為null時,不會考慮我們的自定義序列化器。因此,這裏我們可以放心。
接下來,我們將註冊我們的自定義序列化器,以便Jackson能夠識別並使用它。
如果我們需要這種行為僅限於少量字段,我們可以使用字段級別的配置,通過@JsonSerialize註解來實現。
相應地,讓我們為paused和over這兩個Boolean字段進行標註:
@JsonSerialize(using = NumericBooleanSerializer.class)
private Boolean paused;
@JsonSerialize(using = NumericBooleanSerializer.class)
private Boolean over;然後,同樣地,我們也在測試方法中嘗試序列化:
ObjectMapper mapper = new ObjectMapper();
Game game = new Game(1L, "My Game");
game.setPaused(true);
game.setOver(false);
String json = mapper.writeValueAsString(game);
assertThat(json)
.isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}");儘管測試方法實現與之前的版本幾乎相同,但我們應該注意圍繞數字值的引號:“paused”:”1″, “over”:”0″。 顯然,這些值實際上是包含數字內容的字符串。
最後,如果我們需要在任何地方執行此自定義序列化,Jackson 支持通過向 ObjectMapper 添加 Jackson 模塊來全局配置序列化器:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(Boolean.class, new NumericBooleanSerializer());
mapper.registerModule(module);
Game game = new Game(1L, "My Game");
game.setPaused(true);
game.setOver(false);
String json = mapper.writeValueAsString(game);
assertThat(json)
.isEqualTo("{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}");因此,Jackson 會將所有 Boolean 類型字段序列化為數字字符串,只要我們使用相同的 ObjectMapper 實例。
4.2. 從數值字符串反序列化
類似於序列化,本節我們將定義一個自定義的反序列化器,將數值字符串解析為 Boolean 值。
讓我們通過擴展 JsonDeserializer 類來創建我們的類 NumericBooleanDeserializer:
public class NumericBooleanDeserializer extends JsonDeserializer<Boolean> {
@Override
public Boolean deserialize(JsonParser p, DeserializationContext ctxt)
throws IOException {
if ("1".equals(p.getText())) {
return Boolean.TRUE;
}
if ("0".equals(p.getText())) {
return Boolean.FALSE;
}
return null;
}
}接下來,我們再次標註我們的 布爾值字段,但這次使用 @JsonDeserialize:
@JsonSerialize(using = NumericBooleanSerializer.class)
@JsonDeserialize(using = NumericBooleanDeserializer.class)
private Boolean paused;
@JsonSerialize(using = NumericBooleanSerializer.class)
@JsonDeserialize(using = NumericBooleanDeserializer.class)
private Boolean over;那麼,讓我們編寫另一個測試方法,以查看 NumericBooleanDeserializer 的實際效果:
ObjectMapper mapper = new ObjectMapper();
String json = "{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}";
Game game = mapper.readValue(json, Game.class);
assertThat(game.isPaused()).isEqualTo(true);
assertThat(game.isOver()).isEqualTo(false);當然,以下是翻譯後的內容:
或者,還可以通過 Jackson 模塊以全局配置的方式使用我們的自定義反序列器。
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Boolean.class, new NumericBooleanDeserializer());
mapper.registerModule(module);
String json = "{\"id\":1,\"name\":\"My Game\",\"paused\":\"1\",\"over\":\"0\"}";
Game game = mapper.readValue(json, Game.class);
assertThat(game.isPaused()).isEqualTo(true);
assertThat(game.isOver()).isEqualTo(false);5. 結論
在本文中,我們描述瞭如何將布爾值序列化為整數和數字字符串,以及如何將它們反序列化回原始值。