在基于 ReSTLet 的 API 中,使用 ReSTLet Jackson 扩展,我尝试将 Java 对象序列化为 XML 和 JSON,但无法通过嵌套获取我期望的格式(现有 API 已发布)列表或多维数组。
这是我的 POJO,它生成正确的 JSON:
@JacksonXmlRootElement( localName = "table")
@JsonInclude( JsonInclude.Include.NON_NULL)
public class TableResponse {
protected List data;
protected String[] columns;
public TableResponse( String[] columns, List<List<String>> data ) {
this.columns = columns;
this.data = data;
}
@JacksonXmlElementWrapper(localName = "data")
@JacksonXmlProperty(localName = "row")
//@CanIAddSomeAnnotationHereForNestedListElements?
public List<List<String>> getData() {
return data;
}
@JacksonXmlElementWrapper(localName = "columns")
@JacksonXmlProperty(localName = "column")
public String[] getColumns() {
return columns;
}
}
TableResponse 的 JSON,我希望看到这样的 JSON:
{
"data": [
[
"Row 1 Cell A",
"Row 1 Cell B"
],
[
"Row 2 Cell A",
"Row 2 Cell B"
],
[
"Row 3 Cell A",
"Row 3 Cell B"
]
],
"columns": [
"Column 1",
"Column 2"
]
}
我希望能够像这样制作 XML:
<table>
<data>
<row>
<value>Row 1 Cell A</value>
<value>Row 1 Cell B</value>
</row>
<row>
<value>Row 2 Cell A</value>
<value>Row 2 Cell B</value>
</row>
<row>
<value>Row 3 Cell A</value>
<value>Row 3 Cell B</value>
</row>
</data>
<columns>
<column>Column 1</column>
<column>Column 2</column>
</columns>
</table>
但是我得到了这个 XML(TableResponse 的 XML),它丢失了一个维度:
<table>
<data>
<row>Row 1 Cell A</row>
<row>Row 1 Cell B</row>
<row>Row 2 Cell A</row>
<row>Row 2 Cell B</row>
<row>Row 3 Cell A</row>
<row>Row 3 Cell B</row>
</data>
<columns>
<column>Column 1</column>
<column>Column 2</column>
</columns>
</table>
使用备用 POJO 结构,我可以实现我期望的嵌套列表的 XML(初始化数据并实例化此结构的类很痛苦),但 JSON 不是我想要的:
@JacksonXmlRootElement( localName = "table")
@JsonInclude( JsonInclude.Include.NON_NULL)
public class TableResponseForXML {
protected List data;
protected String[] columns;
public TableResponseForXML( String[] columns, List<Row> data ) {
this.columns = columns;
this.data = data;
}
@JacksonXmlElementWrapper(localName = "data")
@JacksonXmlProperty(localName = "row")
public List<Row> getData() {
return data;
}
@JacksonXmlElementWrapper(localName = "columns")
@JacksonXmlProperty(localName = "column")
public String[] getColumns() {
return columns;
}
public static class Row {
private List<Value> values;
public Row( List<Value> values ) {
this.values = values;
}
@JacksonXmlElementWrapper(localName = "row", useWrapping = false)
@JacksonXmlProperty(localName = "value")
public List<Value> getValues() {
return values;
}
}
public static class Value {
private String value;
public Value( String value ) {
this.value = value;
}
@JsonValue
public String getValue() {
return value;
}
}
}
TableResponseForXML 的 JSON(对象包装内部列表):
{
"data": [
{
"values": [
"Row 1 Cell A",
"Row 1 Cell B"
]
},
{
"values": [
"Row 2 Cell A",
"Row 2 Cell B"
]
},
{
"values": [
"Row 3 Cell A",
"Row 3 Cell B"
]
}
],
"columns": [
"Column 1",
"Column 2"
]
}
我的项目中的一些依赖项是:
- com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.5.3
- org.reSTLet.jee:org.reSTLet:2.3.5
- org.reSTLet.jee:org.reSTLet.ext.jackson:2.3.5
- org.reSTLet.jee:org.reSTLet.ext.json:2.3.5
有没有办法让嵌套列表按照我期望的方式在 JSON 和 XML 之间使用第一个 POJO 结构工作?多维 JSON 数组比包装每个列表的对象更易于使用,并且是此 API 的现有已发布规范。
附注,我也尝试过 Jackson: different XML and JSON format 中的建议,但未能让我的 XmlAdapter/@XmlJavaTypeAdapter 与 ReSTLet 一起在此处使用。
最佳答案
仅使用 Jackson 注释来处理这两种格式似乎很困难。对于您的用例,我认为您需要实现一个单独处理 JSON 和 XML 的自定义序列化器。
这个序列化器如下所示:
public class TableResponseSerializer extends StdSerializer<TableResponse> {
private MediaType mediaType;
public TableResponseSerializer(MediaType mediaType) {
super(TableResponse.class);
this.mediaType = mediaType;
}
private void serializeJson(TableResponse swe,
JsonGenerator jgen,
SerializerProvider sp) throws IOException, JsonGenerationException {
(...)
}
private void serializeXml(TableResponse swe,
JsonGenerator jgen,
SerializerProvider sp) throws IOException, JsonGenerationException {
(...)
}
@Override
public void serialize(TableResponse swe,
JsonGenerator jgen,
SerializerProvider sp) throws IOException, JsonGenerationException {
if (mediaType.equals(MediaType.APPLICATION_JSON)) {
serializeJson(swe, jgen, sp);
} else if (mediaType.equals(MediaType.TEXT_XML)) {
serializeXml(swe, jgen, sp);
}
}
}
serializeJson 方法将构建 JSON 内容:
private void serializeJson(TableResponse swe,
JsonGenerator jgen,
SerializerProvider sp) throws IOException, JsonGenerationException {
jgen.writeStartObject();
// Data
jgen.writeArrayFieldStart("data");
for (List<String> row : swe.getData()) {
jgen.writeStartArray();
for (String rowElt : row) {
jgen.writeString(rowElt);
}
jgen.writeEndArray();
}
jgen.writeEndArray();
// Columns
jgen.writeArrayFieldStart("columns");
for (String column : swe.getColumns()) {
jgen.writeString(column);
}
jgen.writeEndArray();
jgen.writeEndObject();
}
还有serializeXml,即XML:
private void serializeXml(TableResponse swe,
JsonGenerator jgen,
SerializerProvider sp) throws IOException, JsonGenerationException {
jgen.writeStartObject();
// Data
jgen.writeObjectFieldStart("data");
jgen.writeArrayFieldStart("row");
for (List<String> row : swe.getData()) {
jgen.writeStartObject();
jgen.writeArrayFieldStart("value");
for (String rowElt : row) {
jgen.writeString(rowElt);
}
jgen.writeEndArray();
jgen.writeEndObject();
}
jgen.writeEndArray();
jgen.writeEndObject();
// Columns
jgen.writeObjectFieldStart("columns");
jgen.writeArrayFieldStart("column");
for (String column : swe.getColumns()) {
jgen.writeString(column);
}
jgen.writeEndArray();
jgen.writeEndObject();
jgen.writeEndObject();
}
最后一步是在 ReSTLet Jackson 转换器使用的 ObjectMapper
实例上配置序列化器。为此,您需要扩展 JacksonConverter
和 JacksonRepresentation
类。
首先是 JacksonRepresentation
类,您可以在其中重写 getObjectMapper
方法来为 TableResponse
类注册序列化程序:
public class CustomJacksonRepresentation<T> extends JacksonRepresentation<T> {
public CustomJacksonRepresentation(MediaType mediaType, T object) {
super(mediaType, object);
}
public CustomJacksonRepresentation(Representation representation,
Class<T> objectClass) {
super(representation, objectClass);
}
public CustomJacksonRepresentation(T object) {
super(object);
}
@Override
protected ObjectMapper createObjectMapper() {
ObjectMapper objectMapper = super.createObjectMapper();
if (getObjectClass().equals(TableResponse.class)) {
SimpleModule mod = new SimpleModule("");
mod.addSerializer(new TableResponseSerializer(getMediaType()));
objectMapper.registerModule(mod);
}
return objectMapper;
}
}
然后,CustomJacksonConverter
类将在必要时使用这种表示形式:
public class CustomJacksonConverter extends JacksonConverter {
protected <T> JacksonRepresentation<T> create(MediaType mediaType, T source) {
return new CustomJacksonRepresentation<T>(mediaType, source);
}
protected <T> JacksonRepresentation<T> create(Representation source,
Class<T> objectClass) {
return new CustomJacksonRepresentation<T>(source, objectClass);
}
}
要注册 CustomJacksonConverter
类,您可以在启动组件之前依赖 Engine
的 getRegisteredConverters
。不要忘记删除默认的 JacksonConverter
。
List<ConverterHelper> converters = Engine.getInstance().getRegisteredConverters();
JacksonConverter jacksonConverter = new JacksonConverter();
for (ConverterHelper converter : converters) {
if (converter instanceof JacksonConverter) {
jacksonConverter = (JacksonConverter) converter;
break;
}
}
if (jacksonConverter!=null) {
converters.remove(jacksonConverter);
converters.add(new CustomJacksonConverter());
}
这样,您将根据内容协商(Accept
header )获得 XML 和 JSON 所需的输出内容。
关于java - 使用 Jackson 将 Java 列表序列化为 XML 和 JSON,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35735397/