Jackson:将枚举值序列化和反序列化为整数

Posted

技术标签:

【中文标题】Jackson:将枚举值序列化和反序列化为整数【英文标题】:Jackson: Serialize and deserialize enum values as integers 【发布时间】:2016-10-16 10:59:09 【问题描述】:

考虑以下枚举和类:

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;
    

并考虑以下主要功能:

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

目前,这个main的输出是:

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

这个输出对我不利,因为我希望它不是state 的字符串"OFF",而是0,这是枚举OFFOFF 的序数值State

所以我想要得到的实际结果是:

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

有没有什么优雅的方法让它表现得这样?

【问题讨论】:

我对无注释实现的抨击。做回复。 pastebin.com/raw/Mvf9Ygq1 ON = 0OFF = 1,恕我直言,这看起来不像是友好的事情......只是说;-) @Julien 我花了 4 年时间,但我终于采纳了你的建议并修复了它:P 【参考方案1】:

如果您想打印枚举的序数,您可以将构造函数更改为接受 int 而不是 State,然后在调用 Machine 时,您可以按以下方式构造它:

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

这将获取传入状态的枚举序数值并打印以下内容

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

【讨论】:

我喜欢这种解决方法。我会等待其他答案,因为可能有更优雅的方法来做到这一点 @TomC 您的第一行代码将容易出现编译时错误,因为参数(最后一个)在实际参数中是整数,而构造函数实际上具有枚举类型.. 谢谢你 @VikrantKashyap 我在回答中特别指出,必须将构造函数从 State 类型更改为 int 请参阅:更改构造函数以接受 int而不是State【参考方案2】:

为了完成,我发布了另一种方式:自定义序列化程序:

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

【讨论】:

【参考方案3】:

它应该通过指定JsonValue mapper 来工作。

public enum State 
    OFF,
    ON,
    UNKNOWN;

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

这也适用于反序列化,如 @JsonValue 注释的 Javadoc 中所述:

注意:当用于 Java 枚举时,另一个特性是该值 由带注释的方法返回的值也被认为是 反序列化,而不仅仅是要序列化为的 JSON 字符串。这是 可能,因为枚举值的集合是恒定的,并且可以 定义映射,但一般不能对 POJO 类型进行;作为 因此,这不用于 POJO 反序列化

【讨论】:

事实上,如果您在上面的toValue() 方法上添加调试语句,您会注意到Jackson 对toValue() 的调用次数与ENUM 值一样多。 这个解决方案只有在我们处理枚举序数时才有效。如果枚举有一个整数字段,@JsonValue 无法工作,我们需要声明一个带有@JsonCreator 注释的静态方法来解析适当的枚举。这是一个相同的开放错误 - github.com/FasterXML/jackson-databind/issues/1850 也可以将 Mixin 用于 Enum,例如interface EnumMixin @JsonValue int ordinal(); - 不要忘记在对象映射器中注册它。【参考方案4】:

你可以使用设置

objectMapper.enable(SerializationFeature.WRITE_ENUMS_USING_INDEX);

完整的测试用例见https://github.com/FasterXML/jackson-databind/blob/master/src/test/java/com/fasterxml/jackson/databind/ser/TestEnumSerialization.java

感谢https://righele.it/2016/01/30/jackson-deserialization-from-json-to-java-enums/的提示

【讨论】:

优秀。这应该是公认的答案,侵入性较小 它更具侵入性,因为它会影响由该 ObjectMapper 序列化的所有枚举,因此您失去了自己控制它的能力...【参考方案5】:

另一种方式:

public enum State 

    @JsonProperty("0")
    OFF,

    @JsonProperty("1")
    ON,

    @JsonProperty("2")
    UNKNOWN

但是,这将产生 "state" : "1" 而不是 "state" : 1(字符串,而不是数字)。大多数情况下都可以的

【讨论】:

它可能会起作用,但这也需要你在每个枚举项之前写@JsonProperty,并自己维护序数。实现一个只返回序数的方法,变成了一种更好的做法,也更健壮。 另一方面,@SomethingSomething 在 OFFUNKNOWN 之间放置一个新的枚举值(或删除现有的枚举值)将破坏序数-based 行为,因此无论如何都需要维护 没错。同意您的答案值得发布【参考方案6】:

你可以这样使用

import com.fasterxml.jackson.annotation.JsonFormat;

@JsonFormat(shape = JsonFormat.Shape.NUMBER)
public enum State 
       OFF,
       ON,
       UNKNOWN

【讨论】:

以上是关于Jackson:将枚举值序列化和反序列化为整数的主要内容,如果未能解决你的问题,请参考以下文章

如何将远程 crate 的枚举序列化和反序列化为数字?

Jackson 使用枚举键、POJO 值反序列化为 Map

枚举的反序列化不起作用 - Jackson [重复]

从 JSON 反序列化 java 枚举

jackson中自定义处理序列化和反序列化

如何在 Spring Boot 中为 Camel 配置 Jackson ObjectMapper