web-dev-qa-db-ja.com

Jackson enumシリアライズおよびデシリアライザ

私は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

私はここで何が足りないのですか?

190
pookieman

@ xbakesx で指摘されているシリアライザ/デシリアライザソリューションは、完全に分離する場合に最適なソリューションです。そのJSON表現からのあなたのenumクラス。

あるいは、自己完結型のソリューションを好む場合は、@JsonCreatorおよび@JsonValueアノテーションに基づく実装がより便利です。

したがって、 @ Stanley の例を活用すると、次のような完全な自己完結型ソリューションになります(Java 6)。 、ジャクソン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
    }
}
245

2015年6月の このコミット (Jackson 2.6.2以降)では、単に次のように書くことができます。

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

単一の引数を取り、@JsonCreatorで注釈を付ける静的ファクトリメソッドを作成する必要があります(Jackson 1.2以降で利用可能)

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

JsonCreatorアノテーション の詳細についてはこちら をご覧ください。

84
Stanley

実際の答え:

Enumのデフォルトのデシリアライザは.name()を使ってデシリアライズするので、@JsonValueを使いません。 @OldCurmudgeonが指摘したように、.name()の値と一致させるには{"event": "FORGOT_PASSWORD"}を渡す必要があります。

他のオプション(writeとreadの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;
    }

}

ちょっと最後に、特にこれをメソッドgetYourValue()でシリアライズする列挙JsonEnumにするために、あなたのシリアライザとデシリアライザはこのように見えるかもしれません:

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

私は非常に素晴らしく簡潔な解決策を見つけました。私の場合のようにenumクラスを変更できないときに特に便利です。次に、特定の機能を有効にしたカスタムObjectMapperを提供します。これらの機能はJackson 1.6以降で利用可能です。それであなたはあなたのenumに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);
    }
}

利用可能なenum関連の機能が他にもあります。こちらを参照してください。

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

30
lagivan

任意の属性に対して逆シリアル化をカスタマイズできます。

処理される属性にannotationJsonDeserialize(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;
    }
}
6
Fernando Gomes

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;
    }
  }
}
4
Sam Berry

これは、マップの代わりに文字列値を使用する別の例です。

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

これを試して。

 
 public enumイベント{
 
 FORGOT_PASSWORD( "パスワードを忘れた"); 
 
プライベート最終文字列値private Event(最後の文字列の説明){
 this.value = description; 
} 
 
 private Event(){[.____ this.value = this.name(); 
 
 
 @JsonValue 
 final String value(){
 return this.value ; 
} 
} 
3
Durga

列挙型のコンテキストでは、@JsonValue now(2.0以降)を使用すると、直列化およびの直列化復元に使用できます。

@JsonValuejackson-annotationsのjavadocによると、

注:Java列挙型を使用する場合、1つの追加機能として、シリアライズするJSON Stringだけでなく、注釈付きメソッドによって返される値もデシリアライズする値と見なされることがあります。 Enum値のセットは定数であり、マッピングを定義することは可能であるため、これは可能ですが、POJO型では一般的にはできません。そのため、これはPOJOの逆シリアル化には使用されません。

したがって、Event列挙型に上記のようにアノテーションを付けることは、(serializationとdeserializationの両方のために)jackson 2.0+でうまくいきます。

3
Brice Roncace

@JsonSerialize @JsonDeserializeを使用する以外に、オブジェクトマッパーでSerializationFeatureおよびDeserializationFeature(jacksonバインディング)を使用することもできます。

DeserializationFeature.READ_UNKNOWN_ENUM_VALUES_USING_DEFAULT_VALUEなど。指定されたものがenumクラスで定義されていない場合はデフォルトのenum型を指定します。

2
Yuantao

私が見つけた最も簡単な方法は、enumに@ JsonFormat.Shape.OBJECTアノテーションを使うことです。

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