jackson枚举序列化和DeSerializer

我使用JAVA 1.6和jackson1.9.9我有一个枚举

public enum Event { FORGOT_PASSWORD("forgot password"); private final String value; private Event(final String description) { this.value = description; } @JsonValue final String value() { return this.value; } } 

我已经添加了一个@JsonValue,这似乎做了它的序列化对象的工作:

 {"event":"forgot password"} 

但是当我尝试反序列化时,我得到了一个

 Caused by: org.codehaus.jackson.map.JsonMappingException: Can not construct instance of com.globalrelay.gas.appsjson.authportal.Event from String value 'forgot password': value not one of declared Enum instance names 

我在这里错过了什么?

如果你想完全parsing它的JSON表示的枚举类,xbakesx指出的序列化/解串器解决scheme是一个很好的解决scheme。

或者,如果您更喜欢自包含的解决scheme,则基于@JsonCreator和@JsonValue注释的实现将更为方便。

因此,利用Stanley的例子,以下是一个完整的独立解决scheme(Java 6,Jackson 1.9):

 public enum DeviceScheduleFormat { Weekday, EvenOdd, Interval; private static Map<String, DeviceScheduleFormat> namesMap = new HashMap<String, DeviceScheduleFormat>(3); static { namesMap.put("weekday", Weekday); namesMap.put("even-odd", EvenOdd); namesMap.put("interval", Interval); } @JsonCreator public static DeviceScheduleFormat forValue(String value) { return namesMap.get(StringUtils.lowerCase(value)); } @JsonValue public String toValue() { for (Entry<String, DeviceScheduleFormat> entry : namesMap.entrySet()) { if (entry.getValue() == this) return entry.getKey(); } return null; // or fail } } 

请注意,截至2015年6月(jackson2.6.2及以上版本),您现在可以简单地写下:

 public enum Event { @JsonProperty("forgot password") FORGOT_PASSWORD; } 

你应该创build一个静态的工厂方法,它只需要一个参数,并用@JsonCreator注释它(从Jackson 1.2开始)

 @JsonCreator public static Event forValue(String value) { ... } 

在这里阅读更多关于JsonCreator的注释。

实际答案:

默认的枚举解串器使用.name()来反序列化,所以它不使用@JsonValue 。 正如@OldCurmudgeon所指出的那样,你需要传递{"event": "FORGOT_PASSWORD"}来匹配.name()值。

另一个选项(假设你想写和读JSON值是相同的)…

更多信息:

还有另一种方式来pipe理Jackson的序列化和反序列化过程。 您可以指定这些注释以使用您自己的自定义序列化器和反序列化器:

 @JsonSerialize(using = MySerializer.class) @JsonDeserialize(using = MyDeserializer.class) public final class MyClass { ... } 

然后你必须写MySerializerMyDeserializer ,看起来像这样:

MySerializer

 public final class MySerializer extends JsonSerializer<MyClass> { @Override public void serialize(final MyClass yourClassHere, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException { // here you'd write data to the stream with gen.write...() methods } } 

MyDeserializer

 public final class MyDeserializer extends org.codehaus.jackson.map.JsonDeserializer<MyClass> { @Override public MyClass deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException { // then you'd do something like parser.getInt() or whatever to pull data off the parser return null; } } 

最后一点,特别是对于使用方法getYourValue()序列化的枚举JsonEnum ,您的序列化器和反序列化器可能如下所示:

 public void serialize(final JsonEnum enumValue, final JsonGenerator gen, final SerializerProvider serializer) throws IOException, JsonProcessingException { gen.writeString(enumValue.getYourValue()); } public JsonEnum deserialize(final JsonParser parser, final DeserializationContext context) throws IOException, JsonProcessingException { final String jsonValue = parser.getText(); for (final JsonEnum enumValue : JsonEnum.values()) { if (enumValue.getYourValue().equals(jsonValue)) { return enumValue; } } return null; } 

我发现了一个非常好的和简洁的解决scheme,当你不能像在我的情况下那样修改枚举类的时候特别有用。 那么你应该提供一个自定义的ObjectMapper,并启用了一个特定的function。 这些function从jackson1.6以后就可以使用 所以你只需要在你的枚举中写入toString()方法。

 public class CustomObjectMapper extends ObjectMapper { @PostConstruct public void customConfiguration() { // Uses Enum.toString() for serialization of an Enum this.enable(WRITE_ENUMS_USING_TO_STRING); // Uses Enum.toString() for deserialization of an Enum this.enable(READ_ENUMS_USING_TO_STRING); } } 

有更多的枚举相关的function可用,请参阅: http : //wiki.fasterxml.com/JacksonFeaturesSerialization http://wiki.fasterxml.com/JacksonFeaturesDeserialization

这是另一个使用string值而不是地图的例子。

 public enum Operator { EQUAL(new String[]{"=","==","==="}), NOT_EQUAL(new String[]{"!=","<>"}), LESS_THAN(new String[]{"<"}), LESS_THAN_EQUAL(new String[]{"<="}), GREATER_THAN(new String[]{">"}), GREATER_THAN_EQUAL(new String[]{">="}), EXISTS(new String[]{"not null", "exists"}), NOT_EXISTS(new String[]{"is null", "not exists"}), MATCH(new String[]{"match"}); private String[] value; Operator(String[] value) { this.value = value; } @JsonValue public String toStringOperator(){ return value[0]; } @JsonCreator public static Operator fromStringOperator(String stringOperator) { if(stringOperator != null) { for(Operator operator : Operator.values()) { for(String operatorString : operator.value) { if (stringOperator.equalsIgnoreCase(operatorString)) { return operator; } } } } return null; } } 

有多种方法可以完成将JSON对象反序列化为枚举。 我最喜欢的风格是做一个内心阶层:

 import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import org.hibernate.validator.constraints.NotEmpty; import java.util.Arrays; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import static com.fasterxml.jackson.annotation.JsonFormat.Shape.OBJECT; @JsonFormat(shape = OBJECT) public enum FinancialAccountSubAccountType { MAIN("Main"), MAIN_DISCOUNT("Main Discount"); private final static Map<String, FinancialAccountSubAccountType> ENUM_NAME_MAP; static { ENUM_NAME_MAP = Arrays.stream(FinancialAccountSubAccountType.values()) .collect(Collectors.toMap( Enum::name, Function.identity())); } private final String displayName; FinancialAccountSubAccountType(String displayName) { this.displayName = displayName; } @JsonCreator public static FinancialAccountSubAccountType fromJson(Request request) { return ENUM_NAME_MAP.get(request.getCode()); } @JsonProperty("name") public String getDisplayName() { return displayName; } private static class Request { @NotEmpty(message = "Financial account sub-account type code is required") private final String code; private final String displayName; @JsonCreator private Request(@JsonProperty("code") String code, @JsonProperty("name") String displayName) { this.code = code; this.displayName = displayName; } public String getCode() { return code; } @JsonProperty("name") public String getDisplayName() { return displayName; } } } 

您可以自定义任何属性的反序列化。

使用annotationJsonDeserialize( import com.fasterxml.jackson.databind.annotation.JsonDeserialize )声明将要处理的属性的反序列化类。 如果这是一个枚举:

 @JsonDeserialize(using = MyEnumDeserialize.class) private MyEnum myEnum; 

这样你的类将被用来反序列化属性。 这是一个完整的例子:

 public class MyEnumDeserialize extends JsonDeserializer<MyEnum> { @Override public MyEnum deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { JsonNode node = jsonParser.getCodec().readTree(jsonParser); MyEnum type = null; try{ if(node.get("attr") != null){ type = MyEnum.get(Long.parseLong(node.get("attr").asText())); if (type != null) { return type; } } }catch(Exception e){ type = null; } return type; } }