【问题标题】:Jackson deserialize Enums with multiple names杰克逊反序列化具有多个名称的枚举
【发布时间】:2015-06-11 19:55:19
【问题描述】:

我在反序列化具有多个名称的值的枚举时遇到问题。下面是一个例子:Info 是一个 Java 类,里面有一个具有多个名称的枚举:

public class Info {
    //...
    private ContainerFormat format;
}

// ContainerFormat.java:

public enum ContainerFormat {
    //  ....
    MP4("mp4", "mpeg4"),
    UNKNOWN("null");

    private String name;
    private List<String> others;

    ContainerFormat(String name) {
        this.name = name;
    }

    /** The service does not always return the same String for output formats.
     * This 'other' string fixes the deserialization issues caused by that.
     */
    ContainerFormat(String name, String... others) {
        this.name = name;
        this.others = new ArrayList<String>();
        for (String other : others) {
            this.others.add(other);
        }
    }

    @JsonValue
    @Override
    public String toString() {
        return name;
    }

    public List<String> otherNames() {
        return others;
    }

    @JsonCreator
    public static ContainerFormat fromValue(String other) throws JsonMappingException {
        for (ContainerFormat format : ContainerFormat.values()) {
            if (format.toString().equalsIgnoreCase(other)) {
                return format;
            }
            if (format.otherNames() != null && format.otherNames().contains(other)) {
                return format;
            }
        }
        return UNKNOWN;
    }
}

问题是当我反序列化包含“mpeg4”而不是 mp4 的内容时,我收到此错误:

com.fasterxml.jackson.databind.exc.InvalidFormatException: Can not construct instance of com.foo.ContainerFormat from String value 'mpeg4': value not one of declared Enum instance names
 at [Source: N/A; line: -1, column: -1] (through reference chain: com.foo.Info["format"])
    at com.fasterxml.jackson.databind.exc.InvalidFormatException.from(InvalidFormatException.java:55)
    at com.fasterxml.jackson.databind.DeserializationContext.weirdStringException(DeserializationContext.java:650)
    at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:85)
    at com.fasterxml.jackson.databind.deser.std.EnumDeserializer.deserialize(EnumDeserializer.java:20)
    at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:375)
    at com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:98)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:308)
    at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:121)
    at com.fasterxml.jackson.databind.ObjectMapper._readValue(ObjectMapper.java:2769)
    at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:1478)
    at com.fasterxml.jackson.databind.ObjectMapper.treeToValue(ObjectMapper.java:1811)

关于如何解决这个问题的任何指示?

TIA

【问题讨论】:

    标签: java json enums jackson deserialization


    【解决方案1】:

    我根据弗洛林的回答找到了一个很好的解决方案:

    jackson 2.7.0-rc2 的正确配置(可能也在之前)

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

    在您的枚举中,您只需覆盖 toString() 方法:

    public enum EXAMPLE_TYPE {
    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;
        }
    }
    

    您不需要任何注释或自定义反序列化程序。

    【讨论】:

      【解决方案2】:

      摆脱 String nameList&lt;String&gt; other 而是只有一个字段 - List&lt;String&gt; names 并使用@JsonValue

      public enum ContainerFormat {
      //  ....
      MP4("mp4", "mpeg4"),
      UNKNOWN("null");
      
      private List<String> names;
      
      ContainerFormat(List<String> names) {
          this.names = new ArrayList<String>(names);
      }
      
      @JsonValue
      public List<String> getNames()
      {
          return this.names;
      }
      
      @JsonCreator
      public static ContainerFormat getContainerFromValue(String value) throws JsonMappingException {
          for (ContainerFormat format : ContainerFormat.values()) {
              if(format.getValues().contains(value))
                  return format; 
          }
          return UNKNOWN;
      }
      

      或者,如果您选择保留现有代码,您可以尝试使用 @JsonValue

      注释 otherValues()

      【讨论】:

      • 我的值只有一个名称:M4A("m4a") 这些不适用于这个建议的解决方案。
      • 这也会给枚举序列化带来问题——我只想写出一个名字,而不是整个列表。
      【解决方案3】:

      好吧,我找到了一种解决方法:其中一个标志做正确的事情并允许我重新读取该 mpeg4:

          mapper.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_NULL_PROPERTIES, false);
          mapper.configure(org.codehaus.jackson.map.SerializationConfig.Feature.WRITE_ENUMS_USING_TO_STRING, true);
          mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.READ_ENUMS_USING_TO_STRING, true);
          mapper.setPropertyNamingStrategy(org.codehaus.jackson.map.PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
          mapper.setSerializationInclusion(org.codehaus.jackson.map.annotate.JsonSerialize.Inclusion.NON_EMPTY);
          mapper.configure(org.codehaus.jackson.map.DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-09-10
        • 1970-01-01
        • 2021-09-10
        • 1970-01-01
        • 2020-11-30
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多