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 { ... }
然后你必须写MySerializer
和MyDeserializer
,看起来像这样:
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; } }