deserialize - java enum rest api
Jackson enum Serializing y DeSerializer (8)
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 intento 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?
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;
}
}
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 here .
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
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<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;
}
}
}
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<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
}
}
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<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;
}
}
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;
}
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 .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<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;
}
}
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;
}