49

Consider the following enum and class:

public enum State {
    OFF,
    ON,
    UNKNOWN
}

public class Machine {
    String name;
    int numCores;
    State state;

    public Machine(String name, int numCores, State state) {
        this.name = name;
        this.numCores = numCores;
        this.state = state;
    }
}

And consider the following main function:

public static void main(String args[]) {
    Machine m = new Machine("Machine 1", 8, State.OFF);
    ObjectMapper mapper = new ObjectMapper();
    String machineAsJsonString = mapper.writeValueAsString(m);
    System.out.println(machineAsJsonString);
}

Currently, the output of this main is:

{"name" : "Machine 1", "numCores" : 8, "state" : "OFF"}

This output is not good for me, as instead of the string "OFF" for state, I would like it to be 0, which is the ordinal value of OFF in the enum State.

So the actual result I want to get is:

{"name" : "Machine 1", "numCores" : 8, "state" : 0}

Is there some elegant way to make it behave this way?

SomethingSomething
  • 11,491
  • 17
  • 68
  • 126

7 Answers7

90

It should work by specifying JsonValue mapper.

public enum State {
    OFF,
    ON,
    UNKNOWN;

    @JsonValue
    public int toValue() {
        return ordinal();
    }
}  

This works for deserialization also, as noted in Javadoc of @JsonValue annotation:

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

SomethingSomething
  • 11,491
  • 17
  • 68
  • 126
user861594
  • 5,733
  • 3
  • 29
  • 45
  • 2
    In fact, if you put a debug statement on `toValue()` method above, you'll notice that Jackson makes as many calls to `toValue()` as many ENUM values are. – Manu Manjunath Jun 15 '16 at 11:28
  • 2
    This solution works only if we are dealing with enum ordinals. If enums have an integer field, `@JsonValue` fails to work and we need to declare a static method with `@JsonCreator` annotation to resolve the appropriate enum. Here is an open bug for the same - https://github.com/FasterXML/jackson-databind/issues/1850 – Pavan Kumar Feb 21 '18 at 07:55
  • 1
    Also it's possible to use Mixin for Enum, e.g. `interface EnumMixin { @JsonValue int ordinal(); }` - do not forget to register it in object mapper. – Valera Dubrava Jun 29 '19 at 09:45
25

You can use setting

objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);

See https://github.com/FasterXML/jackson-databind/blob/master/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java for complete test cases

Thanks to tip at https://righele.it/2016/01/30/jackson-deserialization-from-json-to-java-enums/

Floern
  • 33,559
  • 24
  • 104
  • 119
Martin
  • 1,385
  • 15
  • 21
12

You can use in this way

import com.fasterxml.jackson.annotation.JsonFormat;

@JsonFormat(shape = JsonFormat.Shape.NUMBER)
public enum State {
       OFF,
       ON,
       UNKNOWN
}
SomethingSomething
  • 11,491
  • 17
  • 68
  • 126
dasunse
  • 2,839
  • 1
  • 14
  • 32
5

For completion I post another way: custom serializer:

public class StateSerializer extends JsonSerializer<State> {  
    public void serialize(State value, JsonGenerator generator, SerializerProvider provider) throws IOException, JsonProcessingException {
        generator.writeStartObject();
        generator.writeFieldName("id");
        generator.writeNumber(value.getId());
        generator.writeEndObject();
    }
}

@JsonSerialize(using = StateSerializer.class)
public enum State { 
    ...
    public int getId(){...}
}
Paco Abato
  • 3,920
  • 4
  • 31
  • 54
5

Yet another way:

public enum State {

    @JsonProperty("0")
    OFF,

    @JsonProperty("1")
    ON,

    @JsonProperty("2")
    UNKNOWN
}

However, this will produce {"state" : "1"} instead of {"state" : 1} (string, not numeric). In most cases it's OK

SomethingSomething
  • 11,491
  • 17
  • 68
  • 126
Nikolai Shevchenko
  • 7,083
  • 8
  • 33
  • 42
  • 1
    It may work, but this also requires you to write `@JsonProperty` before each enum item and maintain the ordinal numbers by yourself. Implementing a method that just returns the ordinal, turns to be a much better practice and much more robust. – SomethingSomething Sep 06 '18 at 10:58
  • 2
    @SomethingSomething from the other hand placing a new enum value between, say, *OFF* and *UNKNOWN* (or deleting existing one) will break the ordinal-based behavior, so maintenance will be required anyway – Nikolai Shevchenko Sep 06 '18 at 13:38
  • That's right. Agreed that your answer is worth being posted – SomethingSomething Sep 06 '18 at 14:41
2

If you want to print the ordinal of the enum you can change your constructor to accept an int instead of State and then in your call to Machine you can structure it in the following way:

Machine m = new Machine("Machine 1", 8, State.OFF.ordinal());

This will get the enum ordinal value of the passed in state and print the following

{name='Machine 1', numCores=8, state=0}

SomethingSomething
  • 11,491
  • 17
  • 68
  • 126
Tom C
  • 804
  • 2
  • 11
  • 24
  • I like this workaround. I'll wait for other answers, as there might be more elegant ways to do it – SomethingSomething Jun 15 '16 at 11:12
  • @TomC your first line of code will prone to a compile time error because of argument (last) is integer in actual argument while Constructor having enum type actually .. Thank yo – Vikrant Kashyap Jun 15 '16 at 11:37
  • @VikrantKashyap I specifically stated in my answer that that the constructor would have to be changed from a type of `State` to `int` See: _change your constructor to accept an `int` instead of `State`_ – Tom C Jun 15 '16 at 11:43
0

For integer enums, in kotlin, this works well. In java as well. If you don't want to use ordinals.

enum class State(@get:JsonValue val state: Int) {
          OFF(0),
          ON(1),
          UNKNOWN(-1)
}

Byte code generated is:

@JsonValue
public final int getState() {
  return this.state;

}

Udayaditya Barua
  • 1,151
  • 12
  • 26