ObjectMapperを使用してデフォルトのコンストラクターなしで不変オブジェクトをデシリアライズする方法は?


106

com.fasterxml.jackson.databind.ObjectMapperを使用して、不変オブジェクトをシリアル化および逆シリアル化したいのですが。

不変クラスは次のようになります(3つの内部属性、ゲッター、コンストラクターのみ)。

public final class ImportResultItemImpl implements ImportResultItem {

    private final ImportResultItemType resultType;

    private final String message;

    private final String name;

    public ImportResultItemImpl(String name, ImportResultItemType resultType, String message) {
        super();
        this.resultType = resultType;
        this.message = message;
        this.name = name;
    }

    public ImportResultItemImpl(String name, ImportResultItemType resultType) {
        super();
        this.resultType = resultType;
        this.name = name;
        this.message = null;
    }

    @Override
    public ImportResultItemType getResultType() {
        return this.resultType;
    }

    @Override
    public String getMessage() {
        return this.message;
    }

    @Override
    public String getName() {
        return this.name;
    }
}

ただし、この単体テストを実行すると:

@Test
public void testObjectMapper() throws Exception {
    ImportResultItemImpl originalItem = new ImportResultItemImpl("Name1", ImportResultItemType.SUCCESS);
    String serialized = new ObjectMapper().writeValueAsString((ImportResultItemImpl) originalItem);
    System.out.println("serialized: " + serialized);

    //this line will throw exception
    ImportResultItemImpl deserialized = new ObjectMapper().readValue(serialized, ImportResultItemImpl.class);
}

私はこの例外を受け取ります:

com.fasterxml.jackson.databind.JsonMappingException: No suitable constructor found for type [simple type, class eu.ibacz.pdkd.core.service.importcommon.ImportResultItemImpl]: can not instantiate from JSON object (missing default constructor or creator, or perhaps need to add/enable type information?)
 at [Source: {"resultType":"SUCCESS","message":null,"name":"Name1"}; line: 1, column: 2]
    at 
... nothing interesting here

この例外により、デフォルトのコンストラクターを作成するように求められますが、これは不変のオブジェクトであるため、使用したくありません。内部属性をどのように設定しますか?APIのユーザーを完全に混乱させます。

だから私の質問は、デフォルトのコンストラクタなしで不変オブジェクトをどうにかしてデシリアライズできますか?


直列化復元中、デシリアライザーはコンストラクターを認識しないため、デフォルトのコンストラクターにヒットします。このため、デフォルトのコンストラクターを作成する必要がありますが、これは不変性を変更しません。また、クラスが最終ではないこともわかりました。なぜですか?誰でも機能を上書きできますね。
Abhinaba Basu

クラスは最終ではありません。そこで書くのを忘れたので、気づいていただきありがとうございます:)
Michal

回答:


149

Jacksonに逆シリアル化のためのオブジェクトを作成する方法を知らせるには、次のように、コンストラクターの@JsonCreator@JsonPropertyアノテーションを使用します。

@JsonCreator
public ImportResultItemImpl(@JsonProperty("name") String name, 
        @JsonProperty("resultType") ImportResultItemType resultType, 
        @JsonProperty("message") String message) {
    super();
    this.resultType = resultType;
    this.message = message;
    this.name = name;
}

1
+1サーゲイに感謝します。動作しましたが、JsonCreatorアノテーションを使用した後でも問題が発生していました。しかし、レビューの結果、リソースでRequestBodyアノテーションを使用するのを忘れていることに気付きました。今それは動作します..もう一度ありがとう。
Rahul

4
POJOにアクセスしてそれを変更することができないために、デフォルトのコンストラクターや@JsonCreatorand @JsonPropertyアノテーションを追加できない場合はどうなりますか?オブジェクトを逆シリアル化する方法はまだありますか?
ジャックストロー

1
@JackStraw 1)オブジェクトからサブクラスを作成し、すべてのゲッターとセッターをオーバーライドします。2)クラスの独自のシリアライザ/デシリアライザを記述して使用します(「カスタムシリアライザ」を検索します)3)オブジェクトをラップし、1つのプロパティに対してのみシリアライザ/デシリアライザを記述します(これにより、シリアライザを記述するよりも作業が少なくなります)クラス自体ですが、そうでない場合もあります)。
ErikE 2017

あなたのコメントを+1してくれて助かった!これまで私が見つけていたすべての解決策は、デフォルトのコンストラクターを作成することでした...
Nilson Aguiar

34

プライベートのデフォルトコンストラクターを使用できます。ジャクソンは、フィールドがプライベートなファイナルであっても、リフレクションを介してフィールドを埋めます。

編集:継承がある場合は、保護された/パッケージで保護されたデフォルトのコンストラクターを親クラスに使用します。


この答えに基づいて、逆シリアル化するクラスが別のクラスを拡張する場合、スーパークラス(または両方のクラス)のデフォルトコンストラクターを保護することができます。
KWILLIAMS 2017年

3

セルゲイ・ペトゥニンの最初の答えは正しいです。ただし、コンストラクターの各パラメーターの冗長な@JsonPropertyアノテーションを削除することで、コードを簡略化できます。

これは、ObjectMapperにcom.fasterxml.jackson.module.paramnames.ParameterNamesModuleを追加することで実行できます。

new ObjectMapper()
        .registerModule(new ParameterNamesModule(JsonCreator.Mode.PROPERTIES))

(ところで、このモジュールはデフォルトでSpringBootに登録されています。JacksonObjectMapperConfigurationからObjectMapper Beanを使用する場合、またはBean Jackson2ObjectMapperBuilderで独自のObjectMapperを作成する場合は、モジュールの手動登録をスキップできます)

例えば:

public class FieldValidationError {
  private final String field;
  private final String error;

  @JsonCreator
  public FieldValidationError(String field,
                              String error) {
    this.field = field;
    this.error = error;
  }

  public String getField() {
    return field;
  }

  public String getError() {
    return error;
  }
}

ObjectMapperはこのjsonをエラーなしでデシリアライズします。

{
  "field": "email",
  "error": "some text"
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.