263

我正在使用 JAVA 1.6 和 Jackson 1.9.9 我有一个枚举

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

我在这里想念什么?

4

17 回答 17

323

如果您希望将枚举类与其 JSON 表示完全解耦, @xbakesx指出的序列化器/反序列化器解决方案是一个很好的解决方案。

或者,如果您更喜欢独立的解决方案,基于@JsonCreator@JsonValue注释的实现会更方便。

因此,利用@Stanley的示例,以下是一个完整的独立解决方案(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
    }
}
于 2013-12-06T10:23:35.357 回答
245

请注意,从 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 注释的替代方法。

于 2015-09-22T17:56:59.033 回答
97

您应该创建一个静态工厂方法,该方法采用单个参数并使用@JsonCreator(自 Jackson 1.2 起可用)对其进行注释

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

在此处阅读有关 JsonCreator 注释的更多信息。

于 2013-07-02T08:21:32.310 回答
47

实际答案:

枚举的默认反序列化器用于.name()反序列化,因此它不使用@JsonValue. 因此,正如@OldCurmudgeon 指出的那样,您需要传入{"event": "FORGOT_PASSWORD"}以匹配该.name()值。

另一个选项(假设您希望写入和读取 json 值相同)...

更多信息:

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

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

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

我的序列化器

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
    }

}

我的反序列化器

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使用 method 序列化的枚举执行此操作时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;
}
于 2012-11-13T21:08:57.560 回答
42

我找到了一个非常简洁明了的解决方案,当您无法像我的情况那样修改枚举类时特别有用。然后,您应该提供一个启用了特定功能的自定义 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

于 2014-07-02T17:37:04.200 回答
12

试试这个。

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;
    }
}
于 2019-04-04T21:29:58.507 回答
8

我喜欢接受的答案。但是,我会对其进行一些改进(考虑到现在有高于版本 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);
        }
    }
于 2021-03-23T16:08:34.163 回答
7

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

使用 annotationJsonDeserialize ( import com.fasterxml.jackson.databind.annotation.JsonDeserialize) 为将要处理的属性声明您的反序列化类。如果这是一个枚举:

@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;
    }
}
于 2015-10-08T21:32:47.333 回答
5

这是另一个使用字符串值而不是映射的示例。

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;
    }
}
于 2016-01-29T23:32:16.283 回答
5

您可以采用多种方法将 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;
    }
  }
}
于 2016-08-31T22:05:55.810 回答
4

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

根据jackson-annotations javadoc@JsonValue

注意:当用于 Java 枚举时,一个附加功能是注释方法返回的值也被认为是要反序列化的值,而不仅仅是要序列化的 JSON 字符串。这是可能的,因为 Enum 值的集合是恒定的,并且可以定义映射,但对于 POJO 类型通常不能这样做;因此,这不用于 POJO 反序列化。

因此,Event使用 jackson 2.0+ 像上面一样对枚举进行注释(对于序列化和反序列化)。

于 2018-07-20T15:26:03.857 回答
3

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

例如 DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUE,如果提供的枚举类型未在枚举类中定义,则提供默认枚举类型。

于 2019-03-13T18:34:18.057 回答
1

就我而言,这是解决的问题:

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

我希望它有帮助!

于 2020-03-10T14:39:41.947 回答
0

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

@JsonFormat(shape = JsonFormat.Shape.OBJECT)
public enum MyEnum{
    ....
}
于 2019-05-29T12:00:41.070 回答
0

我是这样做的:

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


}

因此 LoggingDto 类的属性“logType”的值将是 DATA3_LOG

于 2020-07-02T09:55:15.407 回答
0

这篇文章很旧,但如果它可以帮助某人,请使用 JsonFormat.Shape.STRING

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

代码结果是这样的

{"someenum":"SOME_PROPERTY"}
于 2021-08-27T13:05:19.027 回答
0
@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"
}
]
于 2021-12-01T05:44:33.410 回答