web-dev-qa-db-ja.com

Jackson xmlモジュール:@JacksonXmlTextプロパティを使用した不変型の逆シリアル化

不変の型をjsonとxmlの両方としてシリアル化したい:

シリアル化されたJSONは次のようなものです。

_{
    "text" : "... the text..."
}
_

シリアル化されたxmlは次のようになります。

_ <asText>_text_</asText>
_

(テキストはxmlの要素テキストであることに注意してください)

Javaオブジェクトは次のようなものです:

_@JsonRootName("asText")
@Accessors(prefix="_")
public static class AsText {

    @JsonProperty("text") @JacksonXmlText
    @Getter private final String _text;

    public AsText(@JsonProperty("text") final String text) {
        _text = text;
    }
}
_

_textプロパティはfinal(したがって、オブジェクトは不変です)であり、xml要素のテキストとしてシリアル化するために_@JacksonXmlText_で注釈が付けられていることに注意してください

オブジェクトは不変であるため、テキストからのコンストラクターが必要であり、コンストラクターの引数には_@JsonProperty_の注釈を付ける必要があります。

_    public AsText(@JsonProperty("text") final String text) {
        _text = text;
    }
_

JSONとの間でシリアル化/逆シリアル化する場合、すべてが正常に機能します... XMLとの間でシリアル化/逆シリアル化するときに問題が発生します。

_ // create the object
 AsText obj = new AsText("_text_");

 // init the mapper
 XmlMapper mapper = new XmlMapper();

 // write as xml
 String xml = mapper.writeValueAsString(obj);
 log.warn("Serialized Xml\n{}",xml);

 // Read from xml
 log.warn("Read from Xml:");
 AsText objReadedFromXml = mapper.readValue(xml,
                                              AsText.class);
 log.warn("Obj readed from serialized xml: {}",
          objReadedFromXml.getClass().getName());
_

例外は次のとおりです。

 com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException:認識されないフィールド ""(クラスr01f.types.url.UrlQueryStringParam)、無視できるものとしてマークされていません(2つの既知のプロパティ: "value"、 "name"]) 

Xmlモジュールでは、オブジェクトのコンストラクターに次のような注釈を付ける必要があるようです。

_    public AsText(@JsonProperty("") final String text) {
        _text = text;
    }
_

しかし、これは機能しません:

 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: `test.types.SerializeAsXmlElementTextTest $ AsText`のインスタンスを構築できません(デフォルトの構築物のようなクリエーターは存在しません):オブジェクト値からデシリアライズできません(デリゲートなし-またはプロパティベースの作成者)

JSONから逆シリアル化するには、コンストラクターの引数にあるアノテーション@JsonProperty("text")が必要です。

...どうすればこれを機能させることができますか

15

プロパティのパブリックゲッターを追加してみてください。これで逆シリアル化の問題が修正されるはずです。

@JsonRootName("asText")
@Accessors(prefix = "_")
public static class AsText {

    @JsonProperty("text")
    @JacksonXmlText
    @Getter
    private final String _text;

    public AsText(@JsonProperty("text") final String text) {
        _text = text;
    }

    public String getText() {
        return _text;
    }
}

実際、これらのバージョンのLombak&Jacksonでは、ゲッターを追加しなくても機能します。

<dependencies>
    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
        <version>2.9.0</version>
    </dependency>

    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.18</version>
    </dependency>
</dependencies>
5

「デリゲートベースまたはプロパティベースのクリエーターがありません」というエラーで同じ問題が発生しました。私の場合、Immutablesバージョン2.5.6に問題がありました。バージョン2.5.5にダウングレードして修正しました。バージョン2.5.6はmvnrepository.comで入手できますが、公式ページでは2.5.5とマークされた安定バージョンと同じです。

3
Jaroslav Kuboš

2018年の更新

このハックは2.9.0で機能しましたが、この後は機能しなくなるようです commit 。それを再び機能させる簡単な方法があるかどうかは明らかではありません。


問題の主な理由は、JSONとXMLのシリアル化を同時に使用しようとしているが、構成が異なるためと思われます。残念ながら、XmlMapperObjectMapperを継承し、すべてのJSON固有の構成を継承します(オーバーライドすることはできますが、XML固有のアノテーションでクリアすることはできません)。これが競合の原因です。これを回避する最も簡単な方法は、コンストラクターで @JsonAlias アノテーションを使用することです。少しハッキーですが、機能します。これが私のために働く最小限の例(Lombokなし)です:

@JsonRootName("asText")
public static class AsText {

    @JsonProperty("text")
    @JacksonXmlText
    private final String _text;

    public AsText(@JsonAlias("") @JsonProperty("text") final String text) {
        _text = text;
    }

    @JsonIgnore
    public String getText() {
        return _text;
    }

    @Override
    public String toString() {
        return "AsText{" +
                "_text='" + _text + '\'' +
                '}';
    }
}

また、ゲッターに@JsonIgnoreを追加したことに注意してください。そうしないと、要求したXML形式を取得できませんでした(LombokのonMethodを使用して、 onX で説明されているように同じことができます)。 )。

簡単なテストの場合:

public static void main(String[] args) throws IOException {
    // create the object
    AsText obj = new AsText("123_text_");

    // init the mapper
    //ObjectMapper mapper = new ObjectMapper();
    XmlMapper mapper = new XmlMapper();

    // write as xml
    String xml = mapper.writeValueAsString(obj);
    System.out.println("Serialized Xml\n" + xml);

    // Read from xml
    AsText objReadedFromXml = mapper.readValue(xml, AsText.class);
    System.out.println("Read from Xml: " + objReadedFromXml);
}

次の出力が得られます。

シリアル化されたXml
<asText> 123_text _ </ asText>
Xmlから読み取る:AsText {_text = '123_text_'}

3
SergGr