Jackson 데이터 바인딩 열거 형 대 / 소문자 구분 안 함 JSON을 deserialize하면 “type”:”json”작동하는 곳에서 실패 “type”:”JSON”합니다.

대소 문자를 구분하지 않는 열거 형 값을 포함하는 JSON 문자열을 어떻게 역 직렬화 할 수 있습니까? (Jackson Databind 사용)

JSON 문자열 :

[{"url": "foo", "type": "json"}]

내 Java POJO :

public static class Endpoint {

    public enum DataType {
        JSON, HTML
    }

    public String url;
    public DataType type;

    public Endpoint() {

    }

}

이 경우 JSON을 deserialize하면 "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();
    }

그리고 그것은 효과가있었습니다. 그러나 이것이 나에게 해킹처럼 보이기 때문에 더 나은 솔루션이 있는지 궁금합니다.

커스텀 디시리얼라이저를 작성할 수도 있지만 열거 형을 사용하는 다양한 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));


답변

버전 2.4.0에서는 모든 Enum 유형에 대한 사용자 지정 serializer를 등록 할 수 있습니다 ( github 문제에 대한 링크 ). 또한 Enum 형식을 인식하는 표준 Enum deserializer를 직접 교체 할 수 있습니다. 다음은 예입니다.

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

산출:

["json","html"]
[JSON, HTML]

답변

잭슨 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

테스트가 포함 된 전체 예제

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

답변

내 프로젝트에서 동일한 문제가 발생하여 문자열 키와 사용 @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;
    }
}

답변

Jackson 2.6부터 간단하게 다음과 같이 할 수 있습니다.

    public enum DataType {
        @JsonProperty("json")
        JSON,
        @JsonProperty("html")
        HTML
    }

전체 예는 이 요점을 참조하십시오 .


답변

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

답변

2.1.xJackson과 함께 Spring Boot 를 2.9사용하는 경우 다음 애플리케이션 속성을 사용하면됩니다.

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


답변

GET 매개 변수 에서 대 / 소문자를 무시하고 Enum을 deserialize하려는 경우 ACCEPT_CASE_INSENSITIVE_ENUMS를 활성화해도 아무런 효과가 없습니다. 이 옵션은 body deserialization 에만 작동하므로 도움이되지 않습니다 . 대신 다음을 시도하십시오.

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

그리고

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverter(new StringToEnumConverter());
    }
}

답변 및 코드 샘플은 여기에서