How To Serialize and Deserialize Enums with Jackson

 

Last modified: November 28, 2019

by Eugen Paraschiv

The early-bird price of the new Learn Spring Security OAuth course packages will increase by $50 on Wednesday:

>> CHECK OUT THE COURSE

1. Overview

This quick tutorial will show how to control the way Java Enums are serialized and deserialized with Jackson 2.

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

 

2. Controlling the Enum Representation

Let’s define the following Enum:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

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.1. Default Enum Representation

By default, Jackson will represent Java Enums as simple String – for example:

1

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

Will result in:

1

"MILE"

What we would like to get when marshaling this Enum to a JSON Object is to give something like:

1

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

3.2. Enum as JSON Object

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

1

2

@JsonFormat(shape = JsonFormat.Shape.OBJECT)

public enum Distance { ... }

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

1

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

3.3. Enums and @JsonValue

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

1

2

3

4

5

6

7

8

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:

1

1609.34

3.4. Custom Serializer for Enum

Before Jackson 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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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

    }

}

We will now apply the serializer to the class that will be serialized:

 

1

2

@JsonSerialize(using = DistanceSerializer.class)

public enum TypeEnum { ... }

Which results in:

1

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

4. Deserializing JSON to Enum

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

1

2

3

4

5

public class City {

     

    private Distance distance;

    ...   

}

Next, we'll discuss the different ways of deserializing a JSON string to an Enum.

4.1. Default Behavior

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

For example, it will deserialize the JSON:

1

{"distance":"KILOMETER"}

To a Distance.KILOMETER object:

1

2

City city = new ObjectMapper().readValue(json, City.class);

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

4.2. Using @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.

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

1

2

3

4

5

6

7

8

public enum Distance {

    ...

 

    @JsonValue

    public double getMeters() {

        return meters;

    }

}

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

1

{"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:

1

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

4.3. Using @JsonProperty

The @JsonProperty annotation is used on enumeration instances:

 

1

2

3

4

5

6

7

8

public enum Distance {

    @JsonProperty("distance-in-km")

    KILOMETER("km", 1000),

    @JsonProperty("distance-in-miles")

    MILE("miles", 1609.34);

  

    ...

}

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

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

1

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

Will be mapped to the Distance.KILOMETER object:

1

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

4.4. Using @JsonCreator

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

Consider the JSON representation:

1

2

3

4

5

6

{

    "distance": {

        "unit":"miles",

        "meters":1609.34

    }

}

Now, let's define the forValues() factory method with the @JsonCreator annotation:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

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 @JsonProperty annotation to bind the JSON fields with the method arguments.

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

1

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

4.5. Using a Custom Deserializer

A custom deserializer can be used if none of the described techniques are available. For example, we might have no 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.

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:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

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;

    }

}

Next, we use the @JsonDeserialize annotation on the Enum to specify our custom deserializer:

 

1

2

3

4

@JsonDeserialize(using = CustomEnumDeserializer.class)

public enum Distance {

   ...

}

And our result is:

1

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

5. Conclusion

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sunday_ding

一分钱也是爱

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值