44

I am using REST web service/Apache Wink with Jackson 1.6.2. How do I annotate an enum field so that Jackson deserializes it?

Inner class

public enum BooleanField
{
    BOOLEAN_TRUE        { public String value() { return "1";} },
    BOOLEAN_FALSE       { public String value() { return "0";} },

Java Bean/Request object

BooleanField locked;
public BooleanField getLocked() {return locked;}

The Jackson docs state that it can do this via @JsonValue/@JsonCreator but provides no examples.

Anyone willing to spill the (java)beans, as it were?

Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78
rakehell
  • 489
  • 1
  • 4
  • 6
  • Similar: [Jackson enum Serializing and DeSerializer](https://stackoverflow.com/questions/12468764/jackson-enum-serializing-and-deserializer) – Vadzim Mar 01 '18 at 13:12

6 Answers6

56

If you are using Jackson 1.9, serialization would be done by:

public enum BooleanField {
   BOOLEAN_TRUE("1")
   ;

   // either add @JsonValue here (if you don't need getter)
   private final String value;

   private BooleanField(String value) { this.value = value; }

   // or here
   @JsonValue public String value() { return value; }

so change you need is to add method to Enum type itself, so all values have it. Not sure if it would work on subtype.

For @JsonCreator, having a static factory method would do it; so adding something like:

@JsonCreator
public static BooleanField forValue(String v) { ... }

Jackson 2.0 will actually support use of just @JsonValue for both, including deserialization.

StaxMan
  • 113,358
  • 34
  • 211
  • 239
55

With Jackson 2.6 or newer, the @JsonProperty annotation can be applied directly to the enum constant to change its serialization:

public enum BooleanField
{
    @JsonProperty("1")
    BOOLEAN_TRUE,
    @JsonProperty("0")
    BOOLEAN_FALSE
}
M. Justin
  • 14,487
  • 7
  • 91
  • 130
softarn
  • 5,327
  • 3
  • 40
  • 54
  • This has been discontinued, don't know the exact version though. – Simrandeep Singh Sep 16 '19 at 13:36
  • @SimrandeepSingh: I'm using Jackson 2.9.9 (via Spring Boot 2.1.6) and it's working just fine. – rene Sep 19 '19 at 13:41
  • @SimrandeepSingh This is still available, and has been since Jackson 2.6, per the [`@JsonProperty`](http://fasterxml.github.io/jackson-annotations/javadoc/2.12/com/fasterxml/jackson/annotation/JsonProperty.html) Javadocs. – M. Justin Jul 12 '21 at 21:19
  • Seems OK with Spring Boot 2.6.7 → Jackson 2.13.2 as well. – Alice M. May 18 '22 at 10:35
23

don't annotate them, just configure your ObjectMapper instance:

private ObjectMapper createObjectMapper() {
    final ObjectMapper mapper = new ObjectMapper();
    // enable toString method of enums to return the value to be mapped
    mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
    mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING);
    return mapper;
}

and in your enum override the toString() method:

public enum SectionType {
START("start"),
MORE("more");

    // the value which is used for matching
    // the json node value with this enum
    private final String value;

    SectionType(final String type) {
        value = type;
    }

    @Override
    public String toString() {
        return value;
    }
}

You don't need any annotations or custom deserializers.

luckyhandler
  • 10,651
  • 3
  • 47
  • 64
3

Actually, according to the docs for JsonValue (Jackson 2.3.3):

NOTE: when use for Java enums, one additional feature is
 * that value returned by annotated method is also considered to be the
 * value to deserialize from, not just JSON String to serialize as.
 * This is possible since set of Enum values is constant and it is possible
 * to define mapping, but can not be done in general for POJO types; as such,
 * this is not used for POJO deserialization. 

So for enums, your deserialization will not work using JsonCreator because JsonValue will be used for both serialization and deserialization. One way to do this for enums is using JsonSetter and JsonGetter.

0
public enum BooleanField
{
    BOOLEAN_TRUE("1"),      
    BOOLEAN_FALSE("0");
    
    private final String value;

    BooleanField( int value ) { this.value = value; }
    
}

Deserializer

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.ObjectCodec;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;

public class BooleanFieldDeserializer extends Json Deserializer<BooleanField> {
    
    public BooleanField deserialize( JsonParser p, DeserializationContext ctx )
    throws IOException 
    {
        // boilerplate code for every deserializer
        ObjectCodec objectCodec = p.getCodec();
        JsonNode node = objectCodec.readTree(p);

        // customizable part for your impl
        String booleanFieldString = node.asText();
        return valueOf( booleanFieldString ); <- Enum-supplied method
    }

Then, in your JavaBean...

@JsonDeserialize(using = BooleanFieldDeserializer.class)
BooleanField locked;
rakehell
  • 323
  • 2
  • 11
-2

The following may work if the enumeration is an array or not. (Only for deserialization)

package com.stack.model;

import java.util.HashMap;
import java.util.Map;

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;

import lombok.Data;

@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonPropertyOrder({ "success", "my-enums" })
public class MyObjectJSON {

    @JsonProperty("sucess")
    private boolean success;

    @JsonProperty("my-enums")
    private MyEnum[] myEnums;


    static enum MyEnum {
        Enum1, Enum2, Enum3, Enum4, EnumN;

        private static Map<String, MyEnum> myEnumsMap = new HashMap<String, MyEnum>(5);

        static {
            myEnumsMap.put("enum1-val", Enum1);
            myEnumsMap.put("enum2-val", Enum2);
            myEnumsMap.put("enum3-val", Enum3);
            myEnumsMap.put("enum4-val", Enum4);
            myEnumsMap.put("enumn-val", EnumN);
        }

        @JsonCreator
        public static MyEnum forValue(String value) {
            return myEnumsMap.get(value.toLowerCase());
        }
    }
}

To consider:

  1. The @Data annotation generates setters, getters, toString, etc.
  2. @JsonProperty("my-enums") private MyEnum[] myEnums, this is the way to annotate with jackson the field that is of type Enum ( It works if it is an array or not).

  3. MyEnum is the enumeration of the values ​​to be mapped of the JSON object, suppose the following object:

    { "sucess": true, "my-enums": ["enum1-val", "enum3-val"] }

  4. The forValue function allows mapping the string values ​​of the array to Enum, it is annotated with @JsonCreator to indicate a construction factory used in deserialization.