[java] Jackson 열거 형 직렬화 및 DeSerializer

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

내가 여기서 무엇을 놓치고 있습니까?



답변

@xbakesx 가 지적한 serializer / deserializer 솔루션은 열거 형 클래스를 JSON 표현에서 완전히 분리하려는 경우 훌륭한 솔루션 입니다.

당신이 기반으로 독립적 인 솔루션 구현을 선호하는 경우 또는, @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
    }
}


답변

주의로 있음 이 커밋 년 6 월 2015 년 (잭슨 2.6.2 이상)에 당신은 지금 간단하게 작성할 수 있습니다 :

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


답변

단일 인수를 사용하여 주석을 추가하는 정적 팩토리 메소드를 작성해야합니다 @JsonCreator(Jackson 1.2부터 사용 가능).

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

JsonCreator 주석에 대한 자세한 내용은 여기를 참조하십시오 .


답변

실제 답변 :

열거 형의 기본 deserializer는 deserialize하는 데 사용하므로 .name()를 사용 하지 않습니다 @JsonValue. @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;
}


답변

매우 훌륭하고 간결한 솔루션을 찾았습니다. 특히 열거 형 클래스를 수정할 수없는 경우에 유용합니다. 그런 다음 특정 기능이 활성화 된 사용자 정의 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


답변

이 시도.

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


답변

모든 속성의 역 직렬화를 사용자 정의 할 수 있습니다.

import com.fasterxml.jackson.databind.annotation.JsonDeserialize처리 할 속성에 annotationJsonDeserialize ( )를 사용하여 역 직렬화 클래스를 선언하십시오 . 이것이 열거 형인 경우 :

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