Перечисление Джексона сериализация и десериализатор
Я использую JAVA 1.6 и Jackson 1.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
чего мне здесь не хватает?
9 ответов:
решение сериализатора / десериализатора, указанное xbakesx, является отличным, если вы хотите полностью отделить класс перечисления yor от его представления JSON.
кроме того, если вы предпочитаете автономное решение, реализация на основе аннотаций @JsonCreator и @JsonValue будет более удобной.
таким образом, используя пример Стэнли, следующее является полным автономным решением (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 года (Джексон 2.6.2 и выше) теперь вы можете просто написать:
public enum Event { @JsonProperty("forgot password") FORGOT_PASSWORD; }
вы должны создать статический метод фабрики, который принимает один аргумент и комментировать его с
@JsonCreator(доступно с Джексона 1.2)@JsonCreator public static Event forValue(String value) { ... }подробнее о аннотации JsonCreator здесь.
Правильный Ответ:
десериализатор по умолчанию для перечислений использует
.name()для десериализации, поэтому он не использует@JsonValue. Так как @OldCurmudgeon указал, вам нужно будет пройти в{"event": "FORGOT_PASSWORD"}матч.name()значение.другой вариант (предполагая, что вы хотите, чтобы значения записи и чтения json были одинаковыми)...
Подробнее:
есть (еще) другой способ управления сериализацией и процесс десериализации с Джексоном. Вы можете указать эти заметки, чтобы использовать свой собственный сериализатор и десериализатор:
@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; } }последний немного, особенно для того, чтобы сделать это с перечислением
JsonEnumчто сериализуется с помощью методаgetYourValue(), свой сериализатор и десериализатор может выглядеть так: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; }
Я нашел очень хорошее и краткое решение, особенно полезное, когда вы не можете изменить классы перечисления, как это было в моем случае. Затем вы должны предоставить пользовательский ObjectMapper с включенной определенной функцией. Эти функции доступны с Jackson 1.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); } }доступно больше функций, связанных с перечислением, см. здесь:
https://github.com/FasterXML/jackson-databind/wiki/Serialization-Features https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features
вот еще один пример, который использует строковые значения вместо карты.
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; } }
в контексте перечисления, используя
@JsonValueтеперь (начиная с 2.0) работает для сериализации и десериализации.По словам jackson-аннотации javadoc для
@JsonValue:Примечание: при использовании для перечислений Java одна дополнительная функция заключается в том, что значение, возвращаемое аннотированным методом, также считается значением для десериализации, а не только строкой JSON для сериализации as. Это возможно, так как набор значений перечисления является постоянным и можно определить отображение, но не может быть сделано в целом для типов POJO; как таковой, это не используется для десериализации POJO.
поэтому
Eventперечисление аннотированный просто как выше (для сериализации и десериализации) с Джексоном 2.0+.
Comments