杰克逊databind enum不区分大小写

tom*_*136 80 java enums serialization json jackson

如何反序列化包含不区分大小写的枚举值的JSON字符串?(使用Jackson Databind)

JSON字符串:

[{"url": "foo", "type": "json"}]
Run Code Online (Sandbox Code Playgroud)

和我的Java POJO:

public static class Endpoint {

    public enum DataType {
        JSON, HTML
    }

    public String url;
    public DataType type;

    public Endpoint() {

    }

}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,反序列化JSON "type":"json"将失败,因为它"type":"JSON"可以工作.但我想"json"以命名惯例为理由.

序列化POJO也会导致大写 "type":"JSON"

我想过使用@JsonCreator和@JsonGetter:

    @JsonCreator
    private Endpoint(@JsonProperty("name") String url, @JsonProperty("type") String type) {
        this.url = url;
        this.type = DataType.valueOf(type.toUpperCase());
    }

    //....
    @JsonGetter
    private String getType() {
        return type.name().toLowerCase();
    }
Run Code Online (Sandbox Code Playgroud)

它奏效了.但我想知道是否有更好的解决方案,因为这看起来像是对我的黑客攻击.

我也可以编写一个自定义反序列化器,但是我有很多不同的POJO使用枚举,而且很难维护.

任何人都可以建议一个更好的方法来使用适当的命名约定序列化和反序列化枚举?

我不希望我在java中的枚举是小写的!

这是我使用的一些测试代码:

    String data = "[{\"url\":\"foo\", \"type\":\"json\"}]";
    Endpoint[] arr = new ObjectMapper().readValue(data, Endpoint[].class);
        System.out.println("POJO[]->" + Arrays.toString(arr));
        System.out.println("JSON ->" + new ObjectMapper().writeValueAsString(arr));
Run Code Online (Sandbox Code Playgroud)

dav*_*wil 100

杰克逊2.9

现在使用jackson-databind2.9.0及更高版本非常简单

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);

// objectMapper now deserializes enums in a case-insensitive manner
Run Code Online (Sandbox Code Playgroud)

测试的完整示例

import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

public class Main {

  private enum TestEnum { ONE }
  private static class TestObject { public TestEnum testEnum; }

  public static void main (String[] args) {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS);

    try {
      TestObject uppercase = 
        objectMapper.readValue("{ \"testEnum\": \"ONE\" }", TestObject.class);
      TestObject lowercase = 
        objectMapper.readValue("{ \"testEnum\": \"one\" }", TestObject.class);
      TestObject mixedcase = 
        objectMapper.readValue("{ \"testEnum\": \"oNe\" }", TestObject.class);

      if (uppercase.testEnum != TestEnum.ONE) throw new Exception("cannot deserialize uppercase value");
      if (lowercase.testEnum != TestEnum.ONE) throw new Exception("cannot deserialize lowercase value");
      if (mixedcase.testEnum != TestEnum.ONE) throw new Exception("cannot deserialize mixedcase value");

      System.out.println("Success: all deserializations worked");
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 我正在使用2.9.2,它不起作用.引起:com.fasterxml.jackson.databind.exc.InvalidFormatException:无法反序列化类型的值....性别`来自字符串"male":值不是声明的枚举实例名称之一:[FAMALE,MALE] (7认同)
  • 在 Jackson 2.10.0 中,他们弃用了 `configure(MapperFeature f, boolean state` ,首选模式如下所示: `ObjectMapper mapper = JsonMapper.builder().configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS, true).build()` (4认同)
  • 这是金! (2认同)
  • 使用Spring Boot,您可以简单地添加属性`spring.jackson.mapper.accept-case-insensitive-enums = true` (2认同)

Sam*_*rry 73

我在我的项目中遇到了同样的问题,我们决定使用字符串键构建我们的枚举,并分别使用@JsonValue静态构造函数进行序列化和反序列化.

public enum DataType {
    JSON("json"), 
    HTML("html");

    private String key;

    DataType(String key) {
        this.key = key;
    }

    @JsonCreator
    public static DataType fromString(String key) {
        return key == null
                ? null
                : DataType.valueOf(key.toUpperCase());
    }

    @JsonValue
    public String getKey() {
        return key;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是`DataType.valueOf(key.toUpperCase())` - 否则,你并没有真正改变任何东西。防御性编码以避免 NPE:`return (null == key ? null : DataType.valueOf(key.toUpperCase()))` (2认同)
  • 好抓@sarumont.我做了编辑.此外,将方法重命名为"fromString"到[与JAX-RS很好地配合](http://cxf.apache.org/docs/jax-rs-basics.html#JAX-RSBasics-DealingwithParameters). (2认同)
  • 显然,"key"字段是不必要的.在`getKey`中,你可以只返回name().toLowerCase()` (2认同)
  • 如果您想将枚举命名为与 json 不同的名称,我喜欢使用键字段。就我而言,遗留系统为其发送的值发送一个非常缩写且难以记住的名称,我可以使用此字段为我的 java 枚举转换为更好的名称。 (2认同)

小智 37

从Jackson 2.6开始,您可以简单地执行此操作:

    public enum DataType {
        @JsonProperty("json")
        JSON,
        @JsonProperty("html")
        HTML
    }
Run Code Online (Sandbox Code Playgroud)

有关完整示例,请参阅此要点.

  • 请注意,这样做可以解决问题.现在Jackson只接受小写,并拒绝任何大写或混合大小写的值. (21认同)

Ale*_*lov 33

在2.4.0版中,您可以为所有Enum类型注册自定义序列化程序(链接到github问题).此外,您可以自行更换标准的Enum反序列化器,以便了解Enum类型.这是一个例子:

public class JacksonEnum {

    public static enum DataType {
        JSON, HTML
    }

    public static void main(String[] args) throws IOException {
        List<DataType> types = Arrays.asList(JSON, HTML);
        ObjectMapper mapper = new ObjectMapper();
        SimpleModule module = new SimpleModule();
        module.setDeserializerModifier(new BeanDeserializerModifier() {
            @Override
            public JsonDeserializer<Enum> modifyEnumDeserializer(DeserializationConfig config,
                                                              final JavaType type,
                                                              BeanDescription beanDesc,
                                                              final JsonDeserializer<?> deserializer) {
                return new JsonDeserializer<Enum>() {
                    @Override
                    public Enum deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
                        Class<? extends Enum> rawClass = (Class<Enum<?>>) type.getRawClass();
                        return Enum.valueOf(rawClass, jp.getValueAsString().toUpperCase());
                    }
                };
            }
        });
        module.addSerializer(Enum.class, new StdSerializer<Enum>(Enum.class) {
            @Override
            public void serialize(Enum value, JsonGenerator jgen, SerializerProvider provider) throws IOException {
                jgen.writeString(value.name().toLowerCase());
            }
        });
        mapper.registerModule(module);
        String json = mapper.writeValueAsString(types);
        System.out.println(json);
        List<DataType> types2 = mapper.readValue(json, new TypeReference<List<DataType>>() {});
        System.out.println(types2);
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

["json","html"]
[JSON, HTML]
Run Code Online (Sandbox Code Playgroud)


lin*_*nqu 29

我选择了Sam B.的解决方案,但这是一个更简单的变体.

public enum Type {
    PIZZA, APPLE, PEAR, SOUP;

    @JsonCreator
    public static Type fromString(String key) {
        for(Type type : Type.values()) {
            if(type.name().equalsIgnoreCase(key)) {
                return type;
            }
        }
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)


etS*_*ngh 12

如果您将Spring Boot 2.1.x与Jackson一起2.9使用,则可以简单地使用以下应用程序属性:

spring.jackson.mapper.accept-case-insensitive-enums=true

  • 运行 Spring Boot 2.4.5 和 Jackson 2.11 - 不起作用 (2认同)

Kon*_*bin 7

对于那些试图反序列化 Enum 忽略GET 参数中的大小写的人,启用 ACCEPT_CASE_INSENSITIVE_ENUMS 不会有任何好处。它无济于事,因为此选项仅适用于body deserialization。而是试试这个:

public class StringToEnumConverter implements Converter<String, Modes> {
    @Override
    public Modes convert(String from) {
        return Modes.valueOf(from.toUpperCase());
    }
}
Run Code Online (Sandbox Code Playgroud)

进而

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToEnumConverter());
    }
}
Run Code Online (Sandbox Code Playgroud)

答案和代码示例来自这里


Viv*_*vek 5

要允许在 Jackson 中对枚举进行不区分大小写的反序列化,只需将以下属性添加到application.propertiesSpring Boot 项目的文件中即可。

spring.jackson.mapper.accept-case-insensitive-enums=true
Run Code Online (Sandbox Code Playgroud)

如果您有 yaml 版本的属性文件,请将以下属性添加到您的application.yml文件中。

spring:
  jackson:
    mapper:
      accept-case-insensitive-enums: true
Run Code Online (Sandbox Code Playgroud)