How To Serialize and Deserialize Enums with Jackson – 如何用Jackson对枚举进行序列化和反序列化

最后修改: 2013年 12月 24日

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

1. Overview

1.概述

In this quick tutorial, we’ll learn how to control the way Java Enums are serialized and deserialized with Jackson 2.

在这个快速教程中,我们将学习如何用Jackson 2控制Java枚举的序列化和反序列化的方式

To dig a little deeper and learn other cool things we can do with Jackson 2, head on over to the main Jackson tutorial.

要想深入了解并学习我们可以用杰克逊2做的其他很酷的事情,请前往杰克逊主要教程

2. Controlling the Enum Representation

2.控制枚举的表示方法

Let’s define the following Enum:

让我们定义以下的Enum。

public enum Distance {
    KILOMETER("km", 1000), 
    MILE("miles", 1609.34),
    METER("meters", 1), 
    INCH("inches", 0.0254),
    CENTIMETER("cm", 0.01), 
    MILLIMETER("mm", 0.001);

    private String unit;
    private final double meters;

    private Distance(String unit, double meters) {
        this.unit = unit;
        this.meters = meters;
    }

    // standard getters and setters
}

3. Serializing Enums to JSON

3.将枚举序列化为JSON

3.1. Default Enum Representation

3.1.默认枚举表示法

By default, Jackson will represent Java Enums as a simple String. For instance:

默认情况下,Jackson会将Java Enums表现为一个简单的字符串。比如说。

new ObjectMapper().writeValueAsString(Distance.MILE);

Will result in:

将导致。

"MILE"

However, when marshaling this Enum to a JSON Object, we would like to get something like:

然而,当把这个枚举转换成一个JSON对象时,我们希望得到类似的东西。

{"unit":"miles","meters":1609.34}

3.2. Enum as a JSON Object

3.2.作为JSON对象的枚举

Starting with Jackson 2.1.2, there’s now a configuration option that can handle this kind of representation. This can be done via the @JsonFormat annotation at the class level:

从Jackson 2.1.2开始,现在有一个配置选项可以处理这种表示方法。这可以通过类级别的@JsonFormat注解来完成。

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum Distance { ... }

This will lead to the desired result when serializing this enum for Distance.MILE:

当为Distance.MILE序列化这个enum时,这将导致预期的结果。

{"unit":"miles","meters":1609.34}

3.3. Enums and @JsonValue

3.3.枚举和@JsonValue

Another simple way of controlling the marshaling output for an enum is using the @JsonValue annotation on a getter:

另一个控制枚举输出的简单方法是在一个getter上使用@JsonValue注解。

public enum Distance { 
    ...
 
    @JsonValue
    public String getMeters() {
        return meters;
    }
}

What we’re expressing here is that getMeters() is the actual representation of this enum. So the result of serializing will be:

我们在这里表达的是,getMeters()是这个枚举的实际表示。所以序列化的结果将是。

1609.34

3.4. Custom Serializer for Enum

3.4.Enum的自定义串行器

If we’re using a version of Jackson earlier than 2.1.2, or if even more customization is required for the enum, we can use a custom Jackson serializer. First, we’ll need to define it:

如果我们使用的Jackson版本早于2.1.2,或者需要对枚举进行更多的定制,我们可以使用一个定制的Jackson序列化器。

public class DistanceSerializer extends StdSerializer {
    
    public DistanceSerializer() {
        super(Distance.class);
    }

    public DistanceSerializer(Class t) {
        super(t);
    }

    public void serialize(
      Distance distance, JsonGenerator generator, SerializerProvider provider) 
      throws IOException, JsonProcessingException {
        generator.writeStartObject();
        generator.writeFieldName("name");
        generator.writeString(distance.name());
        generator.writeFieldName("unit");
        generator.writeString(distance.getUnit());
        generator.writeFieldName("meters");
        generator.writeNumber(distance.getMeters());
        generator.writeEndObject();
    }
}

Then we can apply the serializer to the class that’ll be serialized:

然后我们可以将序列化器应用于将要被序列化的类。

@JsonSerialize(using = DistanceSerializer.class)
public enum TypeEnum { ... }

This results in:

这就造成了。

{"name":"MILE","unit":"miles","meters":1609.34}

4. Deserializing JSON to Enum

4.将JSON反序列化为Enum

First, let’s define a City class that has a Distance member:

首先,让我们定义一个城市类,它有一个距离成员。

public class City {
    
    private Distance distance;
    ...    
}

Then we’ll discuss the different ways of deserializing a JSON string to an Enum.

然后我们将讨论将JSON字符串反序列化为Enum的不同方法。

4.1. Default Behavior

4.1.默认行为

By default, Jackson will use the Enum name to deserialize from JSON.

默认情况下,Jackson将使用Enum名称从JSON中反序列化

For example, it’ll deserialize the JSON:

例如,它将对JSON进行反序列化。

{"distance":"KILOMETER"}

To a Distance.KILOMETER object:

到一个Distance.KILOMETER对象。

City city = new ObjectMapper().readValue(json, City.class);
assertEquals(Distance.KILOMETER, city.getDistance());

If we want Jackson to case-insensitively deserialize from JSON by the Enum name, we need to customize the ObjectMapper to enable the ACCEPT_CASE_INSENSITIVE_ENUMS feature.

如果我们想让Jackson通过Enum名称从JSON中进行大小写不敏感的反序列化,我们需要定制ObjectMapper以启用ACCEPT_CASE_INSENSITIVE_ENUMS功能

Let’s say we have another JSON:

比方说,我们有另一个JSON。

{"distance":"KiLoMeTeR"}

Now, let’s do a case-insensitive deserialization:

现在,让我们做一个不区分大小写的反序列化。

ObjectMapper objectMapper = JsonMapper.builder()
  .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS)
  .build();
City city = objectMapper.readValue(json, City.class);
                                                     
assertEquals(Distance.KILOMETER, city.getDistance());

As the test above shows, we enable the ACCEPT_CASE_INSENSITIVE_ENUMS feature with the JsonMapper builder.

如上面的测试所示,我们用JsonMapper构建器启用ACCEPT_CASE_INSENSITIVE_ENUMS功能。

4.2. Using @JsonValue

4.2.使用@JsonValue

We’ve learned how to use @JsonValue to serialize Enums. We can use the same annotation for deserialization as well. This is possible because Enum values are constants.

我们已经学会了如何使用@JsonValue来序列化Enums。我们也可以使用同样的注解来反序列化。这是有可能的,因为Enum的值是常量。

First, let’s use @JsonValue with one of the getter methods, getMeters():

首先,让我们使用@JsonValue和其中一个getter方法,getMeters()

public enum Distance {
    ...

    @JsonValue
    public double getMeters() {
        return meters;
    }
}

The return value of the getMeters() method represents the Enum objects. Therefore, when deserializing the sample JSON:

getMeters()方法的返回值代表Enum对象。因此,在对样本JSON进行反序列化时。

{"distance":"0.0254"}

Jackson will look for the Enum object that has a getMeters() return value of 0.0254. In this case, the object is Distance.INCH:

Jackson将寻找有getMeters()返回值为0.0254的Enum对象。在这种情况下,该对象是Distance.INCH。

assertEquals(Distance.INCH, city.getDistance());

4.3. Using @JsonProperty

4.3.使用@JsonProperty

The @JsonProperty annotation is used on enumeration instances:

@JsonProperty注解用于枚举实例。

public enum Distance {
    @JsonProperty("distance-in-km")
    KILOMETER("km", 1000), 
    @JsonProperty("distance-in-miles")
    MILE("miles", 1609.34);
 
    ...
}

By using this annotation, we’re simply telling Jackson to map the value of the @JsonProperty to the object annotated with this value.

通过使用这个注解,我们只是告诉Jackson将@JsonProperty的值映射到用这个值注解的对象

As a result of the above declaration, the example JSON string:

作为上述声明的结果,例子中的JSON字符串。

{"distance": "distance-in-km"}

Will be mapped to the Distance.KILOMETER object:

将被映射到Distance.KILOMETER对象。

assertEquals(Distance.KILOMETER, city.getDistance());

4.4. Using @JsonCreator

4.4.使用@JsonCreator

Jackson invokes methods annotated with @JsonCreator to get an instance of the enclosing class.

Jackson调用带有@JsonCreator注释的方法,以获得一个包围类的实例。

Consider the JSON representation:

考虑一下JSON的表示方法。

{
    "distance": {
        "unit":"miles", 
        "meters":1609.34
    }
}

Then we’ll define the forValues() factory method with the @JsonCreator annotation:

然后我们将用@JsonCreator注解来定义forValues()工厂方法。

public enum Distance {
   
    @JsonCreator
    public static Distance forValues(@JsonProperty("unit") String unit,
      @JsonProperty("meters") double meters) {
        for (Distance distance : Distance.values()) {
            if (
              distance.unit.equals(unit) && Double.compare(distance.meters, meters) == 0) {
                return distance;
            }
        }

        return null;
    }

    ...
}

Note the use of the @JsonProperty annotation to bind the JSON fields with the method arguments.

注意使用@JsonProperty注解,将JSON字段与方法参数绑定。

Then, when we deserialize the JSON sample, we’ll get the result:

然后,当我们对JSON样本进行反序列化时,我们会得到结果。

assertEquals(Distance.MILE, city.getDistance());

4.5. Using a Custom Deserializer

4.5.使用自定义的解序列器

We can use a custom deserializer if none of the described techniques are available. For example, we might not have access to the Enum source code, or we might be using an older Jackson version that doesn’t support one or more of the annotations covered so far.

如果上述技术都无法使用,我们可以使用一个自定义的反序列化器。例如,我们可能无法访问 Enum 源代码,或者我们可能使用旧的Jackson版本,该版本不支持到目前为止所涉及的一个或多个注释。

According to our custom deserialization article, in order to deserialize the JSON provided in the previous section, we’ll start by creating the deserialization class:

根据我们的自定义反序列化文章,为了反序列化上一节中提供的JSON,我们将从创建反序列化类开始。

public class CustomEnumDeserializer extends StdDeserializer<Distance> {

    @Override
    public Distance deserialize(JsonParser jsonParser, DeserializationContext ctxt)
      throws IOException, JsonProcessingException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);

        String unit = node.get("unit").asText();
        double meters = node.get("meters").asDouble();

        for (Distance distance : Distance.values()) {
           
            if (distance.getUnit().equals(unit) && Double.compare(
              distance.getMeters(), meters) == 0) {
                return distance;
            }
        }

        return null;
    }
}

Then we’ll use the @JsonDeserialize annotation on the Enum to specify our custom deserializer:

然后我们将使用Enum上的@JsonDeserialize注解来指定我们的自定义解序列器。

@JsonDeserialize(using = CustomEnumDeserializer.class)
public enum Distance {
   ...
}

And our result is:

而我们的结果是。

assertEquals(Distance.MILE, city.getDistance());

5. Conclusion

5.结论

This article illustrated how to gain better control over the serialization and deserialization processes and formats of Java Enums.

本文说明了如何更好地控制Java Enums的序列化和反序列化过程和格式

The implementation of all these examples and code snippets can be found over on GitHub.

所有这些示例和代码片段的实现都可以在GitHub上找到over