java - value - spring boot jackson enum




Jackson Enum Serializing e DeSerializer (8)

A solução serializador / desserializador apontada pelo xbakesx é excelente se você deseja separar completamente a classe enum da sua representação JSON.

Como alternativa, se você preferir uma solução independente, uma implementação baseada nas anotações @JsonCreator e @JsonValue seria mais conveniente.

Então, aproveitando o exemplo de Stanley, a seguinte é uma solução completa e independente (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
    }
}

Eu estou usando JAVA 1.6 e Jackson 1.9.9 Eu tenho um enum

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

Eu adicionei um @JsonValue, isso parece fazer o trabalho que serializa o objeto em:

{"event":"forgot password"}

mas quando tento desserializar recebo um

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

O que estou perdendo aqui?


Além de usar @JsonSerialize @JsonDeserialize, você também pode usar SerializationFeature e DeserializationFeature (ligação jackson) no mapeador de objetos.

Como DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, que fornecem o tipo de enumeração padrão, se aquele fornecido não estiver definido na classe enum.


Eu encontrei uma solução muito agradável e concisa, especialmente útil quando você não pode modificar as classes enum como era no meu caso. Então você deve fornecer um ObjectMapper personalizado com um certo recurso ativado. Esses recursos estão disponíveis desde o Jackson 1.6. Portanto, você só precisa escrever o toString() em seu enum.

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

Existem mais recursos relacionados ao enum disponíveis, veja aqui:

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


Existem várias abordagens que você pode realizar para realizar a desserialização de um objeto JSON para um enum. Meu estilo favorito é fazer uma classe 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;
    }
  }
}

Observe que, a partir deste commit em junho de 2015 (Jackson 2.6.2 e acima), você pode agora simplesmente escrever:

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

Tente isso.

public enum Event {

   FORGOT_PASSWORD("forgot password");

    private final String value;

    private Event(final String description) {
        this.value = description;
    }

    private Event() {
        this.value = this.name();
    }

    @JsonValue
    final String value() {
        return this.value;
    }
}

Você pode personalizar a desserialização para qualquer atributo.

Declare sua classe desserializada usando o annotationJsonDeserialize ( import com.fasterxml.jackson.databind.annotation.JsonDeserialize ) para o atributo que será processado. Se isso é um Enum:

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

Dessa forma, sua classe será usada para desserializar o atributo. Este é um exemplo 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;
    }
}

Resposta real:

O desserializador padrão para enums usa .name() para desserializar, portanto, não está usando o @JsonValue . Então, como @OldCurmudgeon apontou, você precisaria passar em {"event": "FORGOT_PASSWORD"} para corresponder ao valor .name() .

Uma outra opção (supondo que você quer que os valores json de escrita e leitura sejam iguais) ...

Mais informações:

Existe (ainda) outra maneira de gerenciar o processo de serialização e desserialização com Jackson. Você pode especificar essas anotações para usar seu próprio serializador e desserializador personalizado:

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

Então você tem que escrever MySerializer e MyDeserializer que se parecem com isto:

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

}

Último pouquinho, particularmente para fazer isso em um enum JsonEnum que serializa com o método getYourValue() , seu serializador e desserializador podem ter esta aparência:

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




jsonserializer