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

时间:2016-03-01 23:06:14

标签: 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:杰克逊 - 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与reslet一起使用。

1 个答案:

答案 0 :(得分:2)

使用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 one,XML one:

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课程,您可以在启动组件之前依赖getRegisteredConverters Engine。不要忘记删除默认的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标题)获得XML和JSON所需的输出内容。