如何在 Jackson 中包装 XML 而不是 JSON



我正在尝试使用Jackson将Foo对象列表序列化为XML和JSON(这是在spring-boot Web应用程序中(。此应用程序正在实现已经存在的 API,因此我必须完全匹配现有的输出格式。我正在寻找的格式如下:

<collection>
<foo>
<some>value</some>
</foo>
<foo>
<some>other</some>
</foo>
</collection>
[
{ "some": "value" },
{ "some": "other" }
]

我正在使用 API 优先的方法,通过编写 OpenAPI 3 规范并从中生成代码。此方法的规范将返回类型定义为Foos 数组:

responses:
'200':
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/foo'
application/xml:
schema:
type: array
items:
$ref: '#/components/schemas/foo'

现在,当我为此生成代码时,我得到了返回List<Foo>的 API 方法。这对 JSON 来说很好,生成我想要的。但是,对于XML,我得到这样的东西:

<List>
<item>
<some>value</some>
</item>
<item>
<some>other</some>
</item>
</List>

由于我必须完全匹配现有的输出格式,因此这是行不通的。结构正确,但名称不正确。在谷歌搜索了大量内容后,我发现我应该创建一个包装类,所以我更改了 API 方法以返回FooCollection并因此定义了包装类:

@XmlRootElement(name="collection")
@JacksonXmlRootElement(localName = "collection")
@XmlAccessorType(XmlAccessType.FIELD)
public class FooCollection {
@JacksonXmlProperty(localName = "foo")
@JacksonXmlElementWrapper(useWrapping = false)
private final List<Foo> values;
public FooCollection() {
this.values = Collections.EMPTY_LIST;
}
public FooCollection(List<Foo> values) {
this.values = values;
}
}

现在我的输出 XML 看起来正确,但 JSON 已损坏:

<collection>
<foo>
<some>value</some>
</foo>
<foo>
<some>other</some>
</foo>
</collection>

和 JSON:

{
"values": [
{ "some": "value" },
{ "some": "other" }
]
}

经过更多搜索后,解决方案是向FooCollection类中的内部List添加一个@JsonValue注释。这样做的结果是正确的 JSON,但现在 XML 再次被破坏(它实际上根本无法产生输出,因为以下不是有效的 XML(:

<foo>
<some>value</some>
</foo>
<foo>
<some>other</some>
</foo>

我希望@JsonValue注释仅在生成 JSON 时生效......或者也许是一种完全不同的方法,因为这个兔子洞越来越深。理想情况下,我想让它在使用List<Foo>返回类型时工作,因为这在语义上是最正确的,但我似乎找不到一种方法在不编写包装类的情况下自定义生成的 XML 上的名称(这会导致我遇到的问题(。

有什么想法吗?

我最终用 JAXB 替换了 Jackson 进行 XML 序列化,这样我就可以更好地控制输出格式。这可以通过使用Spring MVC注册自定义转换器来完成:

converters.add(new JAXBCollectionWrapperHttpMessageConverter("com.mycompany.myapp.model"));
converters.add(new Jaxb2RootElementHttpMessageConverter());
converters.add(new MappingJackson2HttpMessageConverter());

JAXBCollectionWrapperHttpMessageConverter是一个将集合包装到JAXBCollection包装类中的类(为简洁起见,省略错误处理(:

public boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType) {
return Collection.class.isAssignableFrom(clazz) && canWrite(mediaType);
}
protected void writeToResult(Object o, HttpHeaders headers, Result result) {
...
marshaller.marshal(new JaxbCollectionWrapper<>((Collection<?>)o), result);
}

集合包装类如下所示:

@XmlRootElement(name="collection")
public class JaxbCollectionWrapper<T>{
@XmlAnyElement(lax=true)
protected Collection<T> list;
public JaxbCollectionWrapper(){}
public JaxbCollectionWrapper(Collection<T> list){
this.list=list;
}
public Collection<T> getList(){
return list;
}
}

最新更新