2016-03-01 287 views
1

在我的基於Restlet的API中,使用Restlet Jackson擴展,我嘗試將Java對象序列化爲XML和JSON,但無法獲得我期望的格式(即現有API已發佈)與嵌套列表或多維數組。使用Jackson序列化XML列表和JSON列表

這裏是我的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結構,我可以acheive我期待一個嵌套列表的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; 
    } 
    } 
} 

JSON TableResponseForXML的(對象是纏繞在內名單):

{ 
    "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:組織。 restlet.ext.jackson:2.3.5
  • org.restlet.jee:org.restlet.ext.json:2.3.5

有沒有一種方法,使嵌套列表中工作,我期待JSON的方式和第一個POJO結構的XML?與包裝每個列表的對象相比,多維JSON數組的工作更簡單,並且是此API的現有已發佈規範。

一個方面的說明,我也嘗試了Jackson: different XML and JSON format的建議,但未能讓我的XmlAdapter/@ XmlJavaTypeAdapter與restlet一起使用。

+0

我習慣使用GSON來創建JSON字符串,所以我只能說這個。根據我的經驗,我必須對各種數據類型進行大量試驗以獲得我想要的結果,並發現HashMaps 或LinkedHashMaps 以及ArrayList 的組合效果最佳。你可能需要試驗這些來看傑克遜的結果。 – Alan

回答

2

似乎很難處理這兩種格式只使用傑克遜註釋。對於您的用例,我認爲您需要實現一個自定義序列化器,它可以分別處理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(); 
} 

最後一步是在ObjectMapper instanc上配置串行器e由Restlet Jackson轉換器使用。爲此,您需要擴展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頭)您需要的輸出內容XML和JSON。