【问题标题】:Jackson xml module: deserializing immutable type with a @JacksonXmlText propertyJackson xml 模块:使用@JacksonXmlText 属性反序列化不可变类型
【发布时间】:2018-01-23 05:19:24
【问题描述】:

我想将不可变类型序列化为 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(因此该对象是不可变的)并且使用 @JacksonXmlText 进行注释以便序列化为 xml 元素的文本

作为不可变对象,需要文本中的构造函数,并且构造函数的参数必须用@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 个已知属性:“值”、“名称”])

看来xml模块需要对象的构造函数这样注释:

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

但这甚至不起作用:

com.fasterxml.jackson.databind.exc.InvalidDefinitionException:无法构造`test.types.SerializeAsXmlElementTextTest$AsText`的实例(没有像默认构造一样的创建者):无法从对象值反序列化(没有基于委托或属性的创建者)

需要构造函数参数处的注解@JsonProperty("text")从JSON反序列化

...我怎样才能让它工作

【问题讨论】:

  • 更新了我的答案,它不需要吸气剂。这行得通吗?
  • 如果涉及到 xml 反序列化,我正在寻找完全相同的东西。据我所知,除非您更改 @SergGr 提到的 &lt;asText&gt;&lt;text&gt;_text_&lt;/text&gt;&lt;/asText&gt; 之类的 xml 结构,否则这是不可能的。

标签: java xml jackson


【解决方案1】:

尝试为该属性添加一个公共 getter。我相信这应该可以解决反序列化问题。

@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,它也可以在不添加 getter 的情况下工作。

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

【讨论】:

  • 谢谢@Arul,该属性已经有一个getter,因为lombok用于“自动生成”getter属性(注意@Getter注释)我会尝试看看它是否适用一个“编码”的吸气剂,而不是龙目岛的吸气剂
  • 当然,np!我确实看到了 Lombak 的注释。我相信在这种情况下,Jackson 需要一个公共 getter 来进行反序列化。
  • @ArulDhesiaseelan,有一个明确的 getter,它确实可以编译和运行,但输出格式与要求的不同:&lt;asText&gt;&lt;text&gt;_text_&lt;/text&gt;&lt;/asText&gt; 而不是&lt;asText&gt;_text_&lt;/asText&gt;,我不认为这就是OP所需要的。 (在这种修改后的格式中,Jackson 更容易将文本与构造函数参数匹配)
【解决方案2】:

我遇到了同样的错误“没有基于委托或基于属性的创建者”。就我而言,这是 Immutables 2.5.6 版的问题。我已通过降级到 2.5.5 版来修复它。 2.5.6 版本在 mvnrepository.com 中可用,但在官方页面上标记为 2.5.5 的稳定版本。

【讨论】:

  • 谢谢@Jaroslav!我正在使用杰克逊 2.9.0;切换到 2.5.5 对我们来说不是一个可行的选择,但我会试一试,看看它是否是 2.5.5 版本中杰克逊的错误
  • 哦,我的错。我以为您正在使用不可变对象。但是您正在使用 Lombok。我提到的版本与 Immutables 有关。所以没有必要尝试降级杰克逊。很抱歉造成混乱。
【解决方案3】:

2018 年更新

此 hack 在 2.9.0 中有效,但在此 commit 之后似乎停止工作。目前尚不清楚是否有一种简单的方法可以让它再次工作。


看起来您的问题的主要原因是您尝试同时使用 JSON 和 XML 序列化但配置不同。不幸的是,XmlMapper 继承自 ObjectMapper 并继承了所有特定于 JSON 的配置(您可以覆盖它但不能使用特定于 XML 的注释清除它),这就是您发生冲突的原因。似乎解决此问题的最简单方法是在构造函数中使用 @JsonAlias 注释。这有点hacky,但它有效。这是一个适合我的最小示例(没有 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 + '\'' +
                '}';
    }
}

请注意,我还向 getter 添加了 @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
123_text_
从 Xml 读取:AsText{_text='123_text_'}

【讨论】:

  • 你在哪个版本上试过这个?逐字复制您的代码(没有 Lombok),反序列化会抛出 com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid definition for property `` (of type `test.AsText`): Could not find creator property with name '' (known Creator properties: [text]) at [Source: (StringReader); line: 1, column: 1]jackson-dataformat-xml:2.9.4
  • @TWiStErRob,这是一个 hack,它在 2.9.0 中有效,但似乎在 commit 之后停止工作。我不确定是否有一种简单的方法可以让这个 hack 再次起作用。
猜你喜欢
  • 1970-01-01
  • 2018-08-14
  • 1970-01-01
  • 2012-10-18
  • 2015-06-16
  • 1970-01-01
  • 2012-07-24
相关资源
最近更新 更多