wake-up-neo.com

Jackson databind enum Groß- und Kleinschreibung

Wie kann ich eine JSON-Zeichenfolge deserialisieren, die Enummenwerte enthält, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird? (mit Jackson Databind)

Die JSON-Zeichenfolge:

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

und mein Java POJO:

public static class Endpoint {

    public enum DataType {
        JSON, HTML
    }

    public String url;
    public DataType type;

    public Endpoint() {

    }

}

in diesem Fall würde die Deserialisierung des JSON mit "type":"json" dort fehlschlagen, wo "type":"JSON" funktionieren würde . Ich möchte jedoch, dass "json" auch aus Konventionsgründen funktioniert.

Die Serialisierung des POJO führt auch zu Großbuchstaben "type":"JSON"

Ich dachte an @JsonCreator und @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();
    }

Und es hat funktioniert. Aber ich habe mich gefragt, ob es eine bessere Lösung gibt, weil das für mich wie ein Hack aussieht. 

Ich kann auch einen benutzerdefinierten Deserializer schreiben, aber ich habe viele verschiedene POJOs, die Enums verwenden, und es wäre schwer zu warten.

Kann jemand einen besseren Weg vorschlagen, um Enummen mit der richtigen Namenskonvention zu serialisieren und deserialisieren?

Ich möchte nicht, dass meine Enums in Java Kleinbuchstaben sind!

Hier ist ein Testcode, den ich verwendet habe:

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

In Version 2.4.0 können Sie einen benutzerdefinierten Serializer für alle Enum-Typen registrieren ( link zum github-Problem). Sie können auch den standardmäßigen Enum-Deserializer selbst ersetzen, der über den Enum-Typ informiert ist. Hier ist ein Beispiel:

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

Ausgabe:

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

Jackson 2,9  

Dies ist jetzt sehr einfach, mit jackson-databind 2.9.0 und höher

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

// objectMapper now deserializes enums in a case-insensitive manner

Vollständiges Beispiel mit Tests

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

In meinem Projekt bin ich auf dasselbe Problem gestoßen. Wir beschlossen, unser Enum mit einem String-Schlüssel zu erstellen und @JsonValue und einen statischen Konstruktor für die Serialisierung bzw. Deserialisierung zu verwenden.

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;
    }
}
67
Sam Berry

Seit Jackson 2.6 können Sie dies einfach tun:

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

Ein vollständiges Beispiel finden Sie unter this Gist .

29

Ich entschied mich für die Lösung von Sam B. aber eine einfachere Variante.

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

Wenn Sie Spring Boot 2.1.x mit Jackson 2.9 verwenden, können Sie einfach diese Anwendungseigenschaft verwenden:

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

1
etSingh

Ich habe eine Modifikation der Lösung von Iago Fernández und Paul verwendet.

Ich hatte eine Aufzählung in meinem Anforderungsobjekt, bei der die Groß- und Kleinschreibung nicht berücksichtigt werden musste

@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