jackson数据绑定枚举不区分大小写

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

JSONstring:

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

它的工作。 但是我想知道是否有更好的解决scheme,因为这对我来说看起来像一个黑客。

我也可以编写一个自定义的反序列化器,但是我得到了许多不同的使用枚举的POJO,这将很难维护。

任何人都可以提出一个更好的方法来序列化和反序列化具有适当的命名约定枚举?

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

这里是我使用的一些testing代码:

  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版本中,您可以为所有的枚举types注册一个自定义序列化器( 链接到github问题)。 你也可以自己replace标准的枚举解串器,这将会知道枚举types。 这里是一个例子:

 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] 

我在我的项目中遇到了同样的问题,我们决定使用string键构build我们的枚举,分别使用@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; } } 

jackson2.9

从版本2.9.0开始,这就像现在一样简单

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

自从jackson2.6,你可以简单地做到这一点:

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

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

我去找了山姆B的解决scheme,但更简单的变种。

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

问题是相关com.fasterxml.jackson.databind.util.EnumResolver 。 它使用HashMap来保存枚举值,而HashMap不支持不区分大小写的键。

在上面的答案中,所有字符应该是大写或小写。 但我固定所有(在)敏感问题的枚举与:

https://gist.github.com/bhdrk/02307ba8066d26fa1537

CustomDeserializers.java

 import com.fasterxml.jackson.databind.BeanDescription; import com.fasterxml.jackson.databind.DeserializationConfig; import com.fasterxml.jackson.databind.JsonDeserializer; import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.deser.std.EnumDeserializer; import com.fasterxml.jackson.databind.module.SimpleDeserializers; import com.fasterxml.jackson.databind.util.EnumResolver; import java.util.HashMap; import java.util.Map; public class CustomDeserializers extends SimpleDeserializers { @Override @SuppressWarnings("unchecked") public JsonDeserializer<?> findEnumDeserializer(Class<?> type, DeserializationConfig config, BeanDescription beanDesc) throws JsonMappingException { return createDeserializer((Class<Enum>) type); } private <T extends Enum<T>> JsonDeserializer<?> createDeserializer(Class<T> enumCls) { T[] enumValues = enumCls.getEnumConstants(); HashMap<String, T> map = createEnumValuesMap(enumValues); return new EnumDeserializer(new EnumCaseInsensitiveResolver<T>(enumCls, enumValues, map)); } private <T extends Enum<T>> HashMap<String, T> createEnumValuesMap(T[] enumValues) { HashMap<String, T> map = new HashMap<String, T>(); // from last to first, so that in case of duplicate values, first wins for (int i = enumValues.length; --i >= 0; ) { T e = enumValues[i]; map.put(e.toString(), e); } return map; } public static class EnumCaseInsensitiveResolver<T extends Enum<T>> extends EnumResolver<T> { protected EnumCaseInsensitiveResolver(Class<T> enumClass, T[] enums, HashMap<String, T> map) { super(enumClass, enums, map); } @Override public T findEnum(String key) { for (Map.Entry<String, T> entry : _enumsById.entrySet()) { if (entry.getKey().equalsIgnoreCase(key)) { // magic line <-- return entry.getValue(); } } return null; } } } 

用法:

 import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; public class JSON { public static void main(String[] args) { SimpleModule enumModule = new SimpleModule(); enumModule.setDeserializers(new CustomDeserializers()); ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(enumModule); } } 

当我想以不区分大小写的方式进行反序列化(build立在问题中发布的代码上)时,我有时会如何处理枚举:

 @JsonIgnore public void setDataType(DataType dataType) { type = dataType; } @JsonProperty public void setDataType(String dataType) { // Clean up/validate String however you want. I like // org.apache.commons.lang3.StringUtils.trimToEmpty String d = StringUtils.trimToEmpty(dataType).toUpperCase(); setDataType(DataType.valueOf(d)); } 

如果枚举是非平凡的,因此在它自己的类中,我通常会添加一个静态分析方法来处理小写的string。

用jackson反序列化枚举很简单。 当你想反序列化基于string的枚举需要一个构造函数,一个getter和一个setter给你的枚举。使用该枚举的类必须有一个接收数据作为参数,而不是string:

 public class Endpoint { public enum DataType { JSON("json"), HTML("html"); private String type; @JsonValue public String getDataType(){ return type; } @JsonSetter public void setDataType(String t){ type = t.toLowerCase(); } } public String url; public DataType type; public Endpoint() { } public void setType(DataType dataType){ type = dataType; } } 

当你有你的json时,你可以使用Jackson的ObjectMapper反序列化为Endpoint类:

 ObjectMapper mapper = new ObjectMapper(); mapper.enable(SerializationFeature.INDENT_OUTPUT); try { Endpoint endpoint = mapper.readValue("{\"url\":\"foo\",\"type\":\"json\"}", Endpoint.class); } catch (IOException e1) { // TODO Auto-generated catch block e1.printStackTrace(); }