Jackson enum序列化和反序列化

我使用JAVA 1.6和Jackson 1.9.9,我有一个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;
}
}

我已经添加了一个@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

我错过了什么?

324750 次浏览

实际的回答:

枚举的默认反序列化器使用.name()来反序列化,所以它没有使用@JsonValue. c。所以正如@OldCurmudgeon指出的,你需要传入{"event": "FORGOT_PASSWORD"}来匹配.name()值。

另一个选项(假设你想要写入和读取json值是相同的)…

更多信息:

使用Jackson还有另一种方法来管理序列化和反序列化过程。你可以指定这些注释来使用你自己的自定义序列化器和反序列化器:

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

然后你必须写MySerializerMyDeserializer,看起来像这样:

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

你应该创建一个静态工厂方法,它接受一个参数,并使用@JsonCreator注释它(自Jackson 1.2起可用)

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

阅读更多关于JsonCreator注释在这里的信息。

如果你想完全解耦你的枚举类和它的JSON表示,< em > @xbakesx < / em >指出的序列化/反序列化解决方案是一个很好的解决方案。

或者,如果你更喜欢自包含的解决方案,基于@JsonCreator@JsonValue注释的实现会更方便。

因此,利用< em > @Stanley < / em >的例子,下面是一个完整的自包含解决方案(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
}
}

我发现了一个非常好的和简洁的解决方案,特别是当您不能修改枚举类时,就像在我的例子中一样。然后,您应该提供一个启用了特定特性的自定义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 < / p >

注意,从2015年6月的这个承诺 (Jackson 2.6.2及以上版本)开始,你现在可以简单地写:

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

行为记录在这里:https://fasterxml.github.io/jackson-annotations/javadoc/2.11/com/fasterxml/jackson/annotation/JsonProperty.html

从Jackson 2.6开始,这个注释也可以用来改变Enum的序列化,如下所示:

 public enum MyEnum {
@JsonProperty("theFirstValue") THE_FIRST_VALUE,
@JsonProperty("another_value") ANOTHER_VALUE;
}

作为使用JsonValue注释的替代方法。

您可以自定义任何属性的反序列化。

使用将要处理的属性的注解jsondeserialize (import com.fasterxml.jackson.databind.annotation.JsonDeserialize)声明反序列化类。如果这是一个Enum:

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

下面是另一个使用字符串值而不是映射的例子。

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

在枚举的上下文中,现在使用@JsonValue(自2.0起)可用于序列化而且反序列化。

根据jackson-annotations javadoc for @JsonValue:

注意:当用于Java枚举时,一个额外的特性是带注释的方法返回的值也被认为是要反序列化的值,而不仅仅是要序列化的JSON字符串。这是可能的,因为Enum值的集合是常量,可以定义映射,但不能在一般POJO类型;因此,这并不用于POJO反序列化。

因此,在jackson 2.0+中对Event枚举进行如上所述的注释(用于序列化和反序列化)。

除了使用@JsonSerialize @JsonDeserialize,你还可以在对象映射器中使用SerializationFeature和DeserializationFeature (jackson绑定)。

例如DeserializationFeature。READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE,如果提供的枚举类型没有在枚举类中定义,则给出默认的enum类型。

试试这个。

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

我发现最简单的方法是使用@JsonFormat.Shape。枚举的OBJECT注释。

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum{
....
}

以我为例,这就是解决方案:

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


序列化和反序列化以下json:

{
"id": 2,
"name": "WEEKLY"
}

我希望这能有所帮助!

我是这样做的:

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




}

因此属性“logtype”的值;对于类LoggingDto将是DATA3_LOG

我喜欢接受的答案。但是,我想稍微改进一下(考虑到现在有高于版本6的Java可用)。

例子:

    public enum Operation {
EQUAL("eq"),
NOT_EQUAL("ne"),
LESS_THAN("lt"),
GREATER_THAN("gt");


private final String value;


Operation(String value) {
this.value = value;
}


@JsonValue
public String getValue() {
return value;
}


@JsonCreator
public static Operation forValue(String value) {
return Arrays.stream(Operation.values())
.filter(op -> op.getValue().equals(value))
.findFirst()
.orElseThrow(); // depending on requirements: can be .orElse(null);
}
}

这篇文章是旧的,但如果它可以帮助别人,使用JsonFormat.Shape.STRING

@JsonFormat(shape = JsonFormat.Shape.STRING)
public enum SomeEnum{
@JsonProperty("SOME_PROPERTY")
someProperty,
...
}

代码结果如下所示

{"someenum":"SOME_PROPERTY"}
@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum LoginOptionType {


PHONE(1, "Phone"), MAIL(2, "mail"), PERSONAL_EMAIL(3, "Personal email");


private static List<LoginOptionType> all;


static {
all = new ArrayList<LoginOptionType>() {
{
add(LoginOptionType.PHONE);
add(LoginOptionType.MAIL);
add(LoginOptionType.PERSONAL_EMAIL);
}
};
}


private final Integer viewValue;


private final String name;


LoginOptionType(Integer viewValue, String name) {
this.viewValue = viewValue;
this.name = name;
}


public Integer getViewValue() {
return viewValue;
}


public String getName() {
return name;
}


public static List<LoginOptionType> getAll() {
return all;
}
}

响应

[
{
"viewValue": 1,
"name": "Phone"
},
{
"viewValue": 2,
"name": "mail"
},
{
"viewValue": 3,
"name": "Personal email"
}
]

这里,'value'充当反序列化器,'namespace'充当序列化器。因此,你可以传递价值“;学生缺席”;保存时保存到API,在DB中保存为“;student_absent &;”。另一方面,当检索类中的数据时,你的API将返回“Student absent”;

import com.fasterxml.jackson.annotation.JsonProperty;
public enum AttendanceEnums {
STUDENT_PRESENT,
@JsonProperty(value = "Student Absent", namespace = "Student Absent")
STUDENT_ABSENT;
}

我一直在寻找枚举序列化的解决方案,我终于做出了一个解决方案。

https://github.com/sirgilligan/EnumerationSerialization

https://digerati-illuminatus.blogspot.com/2022/10/java-enum-generic-serializer-and.html

它使用了一个新的注释和两个新类EnumerationSerializer和EnumerationDeserializer。您可以子类化EnumerationDeserializer,并创建一个设置枚举class的类(典型方法),或者您可以注释枚举,并且不必拥有EnumerationDeserializer的子类。

@JsonSerialize(using = EnumerationSerializer.class)
@JsonDeserialize(using = EnumerationDeserializer.class)
@EnumJson(serializeProjection = Projection.NAME, deserializationClass = RGB.class)
enum RGB {
RED,
GREEN,
BLUE
}

注意ContextualDeserializer的实现如何从注释中提取类。

https://github.com/sirgilligan/EnumerationSerialization/blob/main/src/main/java/org/example/EnumerationDeserializer.java

这里有很多很好的代码,可以提供一些见解。

对于你的具体问题,你可以这样做:

@JsonSerialize(using = EnumerationSerializer.class)
@JsonDeserialize(using = EnumerationDeserializer.class)
@EnumJson(serializeProjection = Projection.NAME, deserializationClass = Event.class)
public enum Event {
FORGOT_PASSWORD("forgot password");


//This annotation is optional because the code looks for value or alias.
@EnumJson(serializeProjection = Projection.VALUE)
private final String value;


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


}

或者你可以这样做:

@JsonSerialize(using = EnumerationSerializer.class)
@JsonDeserialize(using = EnumerationDeserializer.class)
@EnumJson(serializeProjection = Projection.NAME, deserializationClass = Event.class)
public enum Event {
FORGOT_PASSWORD("forgot password");


private final String value;


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


}

这就是你要做的。

那么如果你有一个类“有”;事件,您可以对每个事件进行注释,以按照您想要的方式进行序列化。

class EventHolder {
@EnumJson(serializeProjection = Projection.NAME)
Event someEvent;


@EnumJson(serializeProjection = Projection.ORDINAL)
Event someOtherEvent;


@EnumJson(serializeProjection = Projection.VALUE)
Event yetAnotherEvent;
}