web-dev-qa-db-ja.com

ジャクソンのデータバインド列挙型の大文字と小文字は区別されません

大文字と小文字を区別しない列挙値を含む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() {

    }

}

この場合、"type":"json"を使用して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();
    }

そしてそれは働いた。しかし、これは私にとってハックのように見えるので、より良い解決策があるかどうか疑問に思っていました。

また、カスタムデシリアライザーを作成することもできますが、enumを使用するさまざまな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));
74
tom91136

バージョン2.4.0では、すべてのEnumタイプにカスタムシリアライザーを登録できます( link github issue)。また、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);
    }
}

出力:

["json","html"]
[JSON, HTML]
33
Alexey Gavrilov

ジャクソン2.9

これはjackson-databind 2.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();
    }
  }
}
91
davnicwil

私は自分のプロジェクトでこの同じ問題に遭遇しました。文字列キーで列挙型を構築し、@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;
    }
}
72
Sam Berry

ジャクソン2.6以降では、これを簡単に行うことができます。

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

完全な例については、 this Gist を参照してください。

36

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

ジャクソン2.1.xでSpring Boot 2.9を使用している場合は、次のアプリケーションプロパティを使用できます。

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

10
etSingh

IagoFernándezand Paulソリューションの修正版を使用しました。

Requestobjectに大文字と小文字を区別する必要がある列挙型がありました

@POST
public Response doSomePostAction(RequestObject object){
 //resource implementation
}



class RequestObject{
 //other params 
 MyEnumType myType;

 @JsonSetter
 public void setMyType(String type){
   MyEnumType.valueOf(type.toUpperCase());
 }
 @JsonGetter
 public String getType(){
   return myType.toString();//this can change 
 }
}
0
trooper31