java - 使用 Jackson 将 Java 列表序列化为 XML 和 JSON

标签 java json xml jackson restlet

在基于 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 实例上配置序列化器。为此,您需要扩展 JacksonConverterJacksonRepresentation 类。

首先是 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 类,您可以在启动组件之前依赖 EnginegetRegisteredConverters。不要忘记删除默认的 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/

相关文章:

java - 如何在 netbeans 项目的文件夹中添加库

json - 如何比较不同顺序的JSON?

javascript - 我可以在 json 数据上使用 javascript 执行复杂的 SQL 样式查询吗?

java - android studio 无法找到 R. 资源,并且 App header 缺乏正确的格式

xml - 无法加载带有 UICollectionVIewCell 数据的 UIView

java - Eclipse RCP - 定义依赖项的所有可能性?

java - 从 ArrayList 中删除子列表

java - 在 Eclipse 中显示值 1.8 不是有效的语言级别

json - 我想使用powershell将convert.d csv加载到json文件文档以进行elasticsearch

xml - 如何在新属性中复制属性值