Jacksonを使用してブール値を整数としてシリアル化および逆シリアル化
1. 序章
Jackson Library は、JSONの処理に関してJavaの世界で事実上の標準です。 ジャクソンの明確に定義されたデフォルトにもかかわらず、ブール値を整数にマッピングするには、手動で構成する必要があります。
確かに、一部の開発者は、これを最善の方法で最小限の労力で達成する方法を考えています。
この記事では、Jacksonでブール値を整数(および数値文字列)としてシリアル化する方法について説明します。
2. シリアル化
最初に、シリアル化の部分を調べます。 ブールから整数のシリアル化をテストするために、モデルGameを定義しましょう。
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);
当然のことながら、 Boolean フィールドの出力はデフォルトになります— 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 にシリアル化する非常に簡単な方法の1つは、Booleanフィールドに@JsonFormatで注釈を付け、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出力でわかるように、ブールフィールド— pausedおよびover —は数値1および
2.2. グローバル構成
すべてのフィールドに注釈を付けることが実用的でない場合があります。 たとえば、要件によっては、ブール値から整数へのシリアル化をグローバルに構成する必要がある場合があります。
幸い、 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文字列をモデルに逆シリアル化するときに、数値からBoolean値を取得することもできます。
幸い、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ですぐにサポートされます。
4. 整数の代わりに数値文字列
もう1つの使用例は、整数の代わりに数値文字列— “ 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はこれを内部で処理し、valueフィールドがnullの場合、カスタムシリアライザーを考慮しません。 したがって、ここでは安全です。
次に、カスタムシリアライザーを登録して、Jacksonがそれを認識して使用できるようにします。
限られた数のフィールドに対してのみこの動作が必要な場合は、@JsonSerializeアノテーションを使用してフィールドレベルの構成を選択できます。
したがって、ブールフィールド、一時停止およびoverに注釈を付けましょう。
@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は、Jackson Modules を介してObjectMapperに追加することにより、シリアライザーのグローバル構成をサポートします。
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は、同じ ObjectMapper インスタンスを使用している限り、すべてのBoolean型フィールドを数値文字列としてシリアル化します。
4.2. 数値文字列からの逆シリアル化
シリアル化と同様に、今回は、数値文字列をブール値に解析するカスタムデシリアライザーを定義します。
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;
}
}
次に、 Boolean フィールドにもう一度注釈を付けますが、今回は@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 Modulesを使用して、カスタムデシリアライザーのグローバル構成を行うこともできます。
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. 結論
この記事では、ブール値を整数と数値文字列にシリアル化する方法と、それらを逆シリアル化する方法について説明しました。
いつものように、サンプルなどのソースコードはGitHubでから入手できます。