Jackson enum Serializing y DeSerializer

Estoy usando JAVA 1.6 y Jackson 1.9.9 Tengo una enumeración

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

Agregué un @JsonValue, esto parece hacer el trabajo en el que serializa el objeto en:

 {"event":"forgot password"} 

pero cuando bash deserializar, obtengo un

 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 

¿Que me estoy perdiendo aqui?

La solución de serializador / deserializador señalada por xbakesx es excelente si desea desacoplar completamente su clase enum de su representación JSON.

Alternativamente, si prefiere una solución independiente, una implementación basada en las anotaciones @JsonCreator y @JsonValue sería más conveniente.

Entonces, aprovechando el ejemplo de Stanley, la siguiente es una solución completa e independiente (Java 6, Jackson 1.9):

 public enum DeviceScheduleFormat { Weekday, EvenOdd, Interval; private static Map namesMap = new HashMap(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 entry : namesMap.entrySet()) { if (entry.getValue() == this) return entry.getKey(); } return null; // or fail } } 

Tenga en cuenta que a partir de este compromiso en junio de 2015 (Jackson 2.6.2 y superior) ahora puede simplemente escribir:

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

Debe crear un método de fábrica estático que tome un solo argumento y anotarlo con @JsonCreator (disponible desde Jackson 1.2)

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

Lea más acerca de la anotación JsonCreator aquí .

Respuesta real:

El deserializador predeterminado para enums usa .name() para deserializar, por lo que no usa @JsonValue . Así como @OldCurmudgeon señaló, necesitaría pasar {"event": "FORGOT_PASSWORD"} para que coincida con el valor de .name() .

Otra opción (suponiendo que quiere que los valores de json de escritura y lectura sean los mismos) …

Más información:

Existe (todavía) otra forma de gestionar el proceso de serialización y deserialización con Jackson. Puede especificar estas anotaciones para usar su propio serializador y deserializador personalizado:

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

Luego tiene que escribir MySerializer y MyDeserializer que se ven así:

MySerializer

 public final class MySerializer extends JsonSerializer { @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 { @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; } } 

Por último, especialmente para hacer esto en un enum JsonEnum que se serializa con el método getYourValue() , su serializador y deserializador podrían verse así:

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

Encontré una solución muy agradable y concisa, especialmente útil cuando no se pueden modificar las clases enum como en mi caso. Luego debe proporcionar un ObjectMapper personalizado con una característica determinada habilitada. Esas características están disponibles desde Jackson 1.6. Entonces, solo necesita escribir el método toString() en su enumeración.

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

Hay más funciones relacionadas con enum disponibles, mira aquí:

https://github.com/FasterXML/jackson-databind/wiki/Serialization-Features https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features

Aquí hay otro ejemplo que usa valores de cadena en lugar de un mapa.

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

Hay varios enfoques que puede tomar para lograr la deserialización de un objeto JSON a una enumeración. Mi estilo favorito es hacer una clase interna:

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

Puede personalizar la deserialización para cualquier atributo.

Declare su clase deserialize utilizando la anotaciónJsonDeserialize ( import com.fasterxml.jackson.databind.annotation.JsonDeserialize ) para el atributo que se procesará. Si esto es un Enum:

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

De esta forma, tu clase se usará para deserializar el atributo. Este es un ejemplo completo:

 public class MyEnumDeserialize extends JsonDeserializer { @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; } } 

En el contexto de una enumeración, usar @JsonValue ahora (desde 2.0) funciona para serialización y deserialización.

De acuerdo con las anotaciones de jackson javadoc para @JsonValue :

NOTA: cuando se usa para enum de Java, una característica adicional es que el valor devuelto por el método anotado también se considera el valor para deserializar, no solo para serializar como String de JSON. Esto es posible ya que el conjunto de valores de Enum es constante y es posible definir el mapeo, pero no se puede hacer en general para los tipos POJO; como tal, esto no se usa para la deserialización POJO.

Por lo tanto, hacer que el enum del Event anote como se muestra arriba (tanto para la serialización como para la deserialización) con jackson 2.0+.