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