Serialize and Deserialize Booleans as Integers With Jackson – 用Jackson将布尔值序列化和反序列化为整数

最后修改: 2021年 11月 26日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.绪论

The Jackson Library is a de facto standard in the Java world when it comes to processing JSON. Despite Jackson’s well-defined defaults, for mapping a Boolean value to Integer, we still need to do manual configurations.

在处理 JSON 时,Jackson 库是 Java 世界中事实上的标准。尽管Jackson有明确的默认值,但对于将Boolean值映射到Integer,我们仍然需要进行手动配置。

Certainly, some developers wonder how to achieve this in the best way and with minimum effort.

当然,一些开发者想知道如何以最佳方式和最小的努力实现这一目标。

In this article, we’ll explain how to serialize Boolean values as Integers — plus, numeric strings — and vice versa in Jackson.

在这篇文章中,我们将解释如何在Jackson中把Boolean值序列化为Integers–另外,数字字符串–反之亦然。

2. Serialization

2.序列化

Initially, we’ll look into the serialization part. To test Boolean to Integer serialization, let’s define our model, Game:

首先,我们来看看序列化的部分。为了测试BooleanInteger的序列化,让我们定义我们的模型,Game

public class Game {

    private Long id;
    private String name;
    private Boolean paused;
    private Boolean over;

    // constructors, getters and setters
}

As usual, the default serialization of the Game object will use Jackson’s ObjectMapper:

像往常一样,Game对象的默认序列化将使用杰克逊的ObjectMapper

ObjectMapper mapper = new ObjectMapper();
Game game = new Game(1L, "My Game");
game.setPaused(true);
game.setOver(false);
String json = mapper.writeValueAsString(game);

Not surprisingly, the output for Boolean fields will be the default — true or false:

毫不奇怪,Boolean字段的输出将是默认的 – truefalse

{"id":1, "name":"My Game", "paused":true, "over":false}

However, we aim to get the following JSON output from our Game object in the end:

然而,我们的目标是最终从我们的Game对象中获得以下JSON输出。

{"id":1, "name":"My Game", "paused":1, "over":0}

2.1. Field Level Configuration

2.1.现场级配置

One pretty straightforward way of serializing into Integer is annotating our Boolean fields with @JsonFormat and setting the Shape.NUMBER for it:

将序列化为Integer的一个相当直接的方法是用@JsonFormat注释我们的Boolean字段,并为其设置Shape.NUMBER

@JsonFormat(shape = Shape.NUMBER)
private Boolean paused;

@JsonFormat(shape = Shape.NUMBER)
private Boolean over;

Then, let’s try our serialization in a test method:

然后,让我们在一个测试方法中试试我们的序列化。

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}");

As we notice in our JSON output, our Boolean fields — paused and over — formed into numbers 1 and 0. We can see that the values are in integer format since they aren’t surrounded by quotes.

正如我们在JSON输出中注意到的,我们的Boolean字段 – pausedover – 形成了数字10。我们可以看到这些值是整数格式的,因为它们没有被引号所包围。

2.2. Global Configuration

2.2.全局配置

Sometimes, annotating every field isn’t practical. For example, depending on requirements, we may need to configure our Boolean to Integer serialization globally.

有时,对每个字段进行注释并不实际。例如,根据要求,我们可能需要在全球范围内将我们的Boolean配置为Integer序列化。

Luckily, Jackson allows us to globally configure @JsonFormat by overriding the defaults in ObjectMapper:

幸运的是,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. Deserialization

3.反序列化

Similarly, we may also want to obtain Boolean values from numbers while deserializing JSON strings into our models.

同样地,在将JSON字符串反序列化到我们的模型中时,我们也可能想从数字中获得Boolean值。

Fortunately, Jackson can parse numbers — only 1 and 0 — into Boolean values by default. So, we don’t need to use @JsonFormat annotation or any additional configuration either.

幸运的是,Jackson可以将数字–只有10–默认解析为Boolean值。所以,我们也不需要使用@JsonFormat注解或任何其他配置。

Thus, with no configuration, let’s see this behavior with the help of another test method:

因此,在没有配置的情况下,让我们在另一个测试方法的帮助下看看这个行为。

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);

Consequently, Integer to Boolean deserialization is supported out of the box in Jackson.

因此,IntegerBoolean的反序列化在Jackson中支持开箱即用。

4. Numeric Strings Instead of Integers

4.数字字符串而不是整数

Another use case is using numeric strings — “1” and “0” — instead of integers. In this case, serializing Boolean values into numeric strings or deserializing them back to Boolean needs some more effort.

另一个用例是使用数字字符串–“1”“0”–而不是整数。在这种情况下,将Boolean值序列化为数字字符串或将其反序列化为Boolean需要更多努力。

4.1. Serializing Into Numeric Strings

4.1.序列化为数字字符串

To serialize a Boolean value into numeric string equivalents, we need to define a custom serializer.

为了将Boolean值序列化为数字字符串等价物,我们需要定义一个自定义序列化器。

So, let’s create our NumericBooleanSerializer by extending Jackson’s JsonSerializer:

因此,让我们通过扩展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");
    }
}

As a side note, normally, Boolean types can be null. However, Jackson handles this internally and doesn’t take our custom serializer into account when the value field is null. Therefore, we’re safe here.

顺便提一下,通常,Boolean类型可以是null。然而,Jackson在内部处理这个问题,当value字段为null时,不会考虑到我们的自定义序列化器。因此,我们在这里是安全的。

Next, we’ll register our custom serializer so that Jackson recognizes and uses it.

接下来,我们将注册我们的自定义序列化器,以便Jackson能够识别并使用它。

If we need this behavior only for a limited number of fields, we can choose the field level configuration with the @JsonSerialize annotation.

如果我们只对有限的字段需要这种行为,我们可以通过@JsonSerialize注解选择字段级配置。

Accordingly, let’s annotate our Boolean fields, paused and over:

因此,让我们注释我们的Boolean字段,pausedover

@JsonSerialize(using = NumericBooleanSerializer.class)
private Boolean paused;

@JsonSerialize(using = NumericBooleanSerializer.class)
private Boolean over;

Then, likewise, we try the serialization in a test method:

然后,同样地,我们在一个测试方法中尝试序列化。

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\"}");

Although the test method implementation is almost identical to the previous ones, we should pay attention to the quotes — “paused”:”1″, “over”:”0″ — around the number values. Surely, this indicates those values are actual strings containing numeric contents.

尽管测试方法的实现与之前的几乎相同,但我们应该注意数字值周围的引号–“paused”: “1″, “over”: “0″。当然,这表明这些值是包含数字内容的实际字符串。

Last but not least, in case we need to perform this custom serialization everywhere, Jackson supports global configuration of serializers by adding them to ObjectMapper via Jackson Modules:

最后但并非最不重要的是,如果我们需要在任何地方执行这种自定义的序列化,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\"}");

As a result, Jackson serializes all Boolean typed fields as numeric strings as long as we use the same ObjectMapper instance.

因此,Jackson将所有Boolean类型的字段序列化为数字字符串,只要我们使用相同的ObjectMapper实例。

4.2. Deserializing From Numeric Strings

4.2.从数字字符串反序列化

Similar to serializing, this time we’ll define a custom deserializer to parse numeric strings into Boolean values.

与序列化类似,这次我们将定义一个自定义反序列化器,将数字字符串解析为Boolean值。

Let’s create our class NumericBooleanDeserializer by extending JsonDeserializer:

让我们通过扩展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;
    }

}

Next, we annotate our Boolean fields once more, but this time with @JsonDeserialize:

接下来,我们再一次注释我们的Boolean字段,但这次是用@JsonDeserialize

@JsonSerialize(using = NumericBooleanSerializer.class)
@JsonDeserialize(using = NumericBooleanDeserializer.class)
private Boolean paused;

@JsonSerialize(using = NumericBooleanSerializer.class)
@JsonDeserialize(using = NumericBooleanDeserializer.class)
private Boolean over;

So, let’s write another test method to see our NumericBooleanDeserializer in action:

所以,让我们再写一个测试方法,看看我们的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);

Alternatively, a global configuration of our custom deserializer is also possible via Jackson Modules:

另外,通过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. Conclusion

5.总结

In this article, we described how to serialize Boolean values into integers and numeric strings and how to deserialize them back.

在这篇文章中,我们描述了如何将Boolean值序列化为整数和数字字符串,以及如何将其反序列化。

As always, the source code for the samples and more are available over on GitHub.

一如既往,样本的源代码和更多内容可在GitHub上获得。