Estou usando JAVA 1.6 e Jackson 1.9.9. 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, ele parece fazer o trabalho de serializar 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?
java
enums
jackson
jsonserializer
pookieman
fonte
fonte
{"Event":"FORGOT_PASSWORD"}
? Observe os limites em Evento e FORGOT_PASSWORD.getValue
dissoGetValue
não funcionaRespostas:
A solução de serializador / desserializador apontada por @xbakesx é excelente se você deseja desacoplar completamente sua classe enum de sua representação JSON.
Como alternativa, se você preferir uma solução independente, uma implementação baseada em
@JsonCreator
e@JsonValue
anotações seria mais conveniente.Portanto, aproveitando o exemplo de @Stanley, o seguinte é uma solução independente completa (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 } }
fonte
Observe que, a partir deste commit em junho de 2015 (Jackson 2.6.2 e superior), agora você pode simplesmente escrever:
public enum Event { @JsonProperty("forgot password") FORGOT_PASSWORD; }
O comportamento está documentado aqui: https://fasterxml.github.io/jackson-annotations/javadoc/2.11/com/fasterxml/jackson/annotation/JsonProperty.html
fonte
Você deve criar um método de fábrica estático que leva um único argumento e anota-o com
@JsonCreator
(disponível desde Jackson 1.2)@JsonCreator public static Event forValue(String value) { ... }
Leia mais sobre a anotação JsonCreator aqui .
fonte
@JSONValue
para serializar e@JSONCreator
desserializar.@JsonCreator public static Event valueOf(int intValue) { ... }
para desserializarint
para oEvent
enumerador.Resposta real:
O desserializador padrão para enums usa
.name()
para desserializar, portanto, não está usando o@JsonValue
. Portanto, como @OldCurmudgeon apontou, você precisa passar{"event": "FORGOT_PASSWORD"}
para corresponder ao.name()
valor.Uma outra opção (supondo que você deseja que os valores de gravação e leitura json sejam os mesmos) ...
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
eMyDeserializer
que se parece 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; } }
Por último, especialmente para fazer isso em um enum
JsonEnum
que serializa com o métodogetYourValue()
, seu serializador e desserializador podem ter a seguinte 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; }
fonte
Eu encontrei uma solução muito boa e concisa, especialmente útil quando você não pode modificar as classes enum como era no meu caso. Em seguida, você deve fornecer um ObjectMapper customizado com um determinado recurso ativado. Esses recursos estão disponíveis desde Jackson 1.6. Portanto, você só precisa escrever o
toString()
método 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 a enum disponíveis, veja aqui:
https://github.com/FasterXML/jackson-databind/wiki/Serialization-Features https://github.com/FasterXML/jackson-databind/wiki/Deserialization-Features
fonte
Experimente 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; } }
fonte
Você pode personalizar a desserialização para qualquer atributo.
Declare sua classe de desserialização usando annotationJsonDeserialize (
import com.fasterxml.jackson.databind.annotation.JsonDeserialize
) para o atributo que será processado. Se este for 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; } }
fonte
Existem várias abordagens que você pode usar 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; } } }
fonte
Aqui está outro exemplo que usa valores de string em vez de um 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; } }
fonte
No contexto de um enum, usar
@JsonValue
now (desde 2.0) funciona para serialização e desserialização.De acordo com o javadoc de jackson-annotations para
@JsonValue
:Portanto, ter o
Event
enum anotado como acima funciona (para serialização e desserialização) com jackson 2.0+.fonte
Além de usar @JsonSerialize @JsonDeserialize, você também pode usar SerializationFeature e DeserializationFeature (vinculação de jackson) no mapeador de objetos.
Como DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE, que fornece o tipo de enum padrão se aquele fornecido não estiver definido na classe de enum.
fonte
No meu caso, foi isso que resolveu:
import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; @JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum PeriodEnum { DAILY(1), WEEKLY(2), ; private final int id; PeriodEnum(int id) { this.id = id; } public int getId() { return id; } public String getName() { return this.name(); } @JsonCreator public static PeriodEnum fromJson(@JsonProperty("name") String name) { return valueOf(name); } }
Serializa e desserializa o seguinte json:
{ "id": 2, "name": "WEEKLY" }
Espero que ajude!
fonte
A maneira mais simples que encontrei é usando a anotação @ JsonFormat.Shape.OBJECT para o enum.
@JsonFormat(shape = JsonFormat.Shape.OBJECT) public enum MyEnum{ .... }
fonte
Eu fiz assim:
// Your JSON {"event":"forgot password"} // Your class to map public class LoggingDto { @JsonProperty(value = "event") private FooEnum logType; } //Your enum public enum FooEnum { DATA_LOG ("Dummy 1"), DATA2_LOG ("Dummy 2"), DATA3_LOG ("forgot password"), DATA4_LOG ("Dummy 4"), DATA5_LOG ("Dummy 5"), UNKNOWN (""); private String fullName; FooEnum(String fullName) { this.fullName = fullName; } public String getFullName() { return fullName; } @JsonCreator public static FooEnum getLogTypeFromFullName(String fullName) { for (FooEnum logType : FooEnum.values()) { if (logType.fullName.equals(fullName)) { return logType; } } return UNKNOWN; } }
Portanto, o valor da propriedade "logType" para a classe LoggingDto será DATA3_LOG
fonte