Reputation: 2792
I am developing spring boot based web services API. I need to return a list of things (ProductData) for the GET response.
This is what the response looks like
<ProductDataList>
<ProductData>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
</ProductData>
</ProductDataList>
But I don't need the extra <ProductData>
tag.
I need the response as below.
<ProductDataList>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
</ProductDataList>
Any idea why an extra tag is generated?
I have below in my WebMvcConfig file.
@Bean
public MappingJackson2XmlHttpMessageConverter xmlConverter() {
Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
builder.propertyNamingStrategy(PropertyNamingStrategy.
PascalCaseStrategy.PASCAL_CASE_TO_CAMEL_CASE);
builder.serializationInclusion(JsonInclude.Include.NON_EMPTY);
builder.failOnUnknownProperties(false);
MappingJackson2XmlHttpMessageConverter xmlConverter =
new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build());
return xmlConverter;
}
In my controller I have
@RequestMapping(value = "/productdata")
@ResponseBody
public ProductDataList getProductData(@RequestParam final String[] ids) {
ArrayList<ProductData> products = productDataService.getProductData(ids);
ProductData[] pdArray = new ProductData[products.size()];
products.toArray(pdArray);
ProductDataList productDataList = new ProductDataList();
productDataList.setProductData(pdArray);
return productDataList;
}
This is my ProductDataList class.
public class ProductDataList{
ProductData[] productData;
public ProductData[] getProductData() {
return productData;
}
public void setProductData(ProductData[] productData) {
this.productData = productData;
}
}
Edit 1.
When I return ArrayList<ProductData>
the response was like this.
<ArrayList>
<item>...</item>
<item>...</item>
<item>...</item>
</ArrayList>
Edit 2.
After adding annotation JsonTypeInfo
I made some progress, but not quite there to what I wanted.
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY)
public class ProductData {}
<ProductDataList>
<item _type="ProductData">...</item>
<item _type="ProductData">...</item>
<item _type="ProductData">...</item>
<ProductDataList>
Upvotes: 0
Views: 5375
Reputation: 2792
After some effort I was able to get this resolved. Key thing is to have @JacksonXmlElementWrapper(useWrapping = false)
in the Object as mentioned in this answer
@JacksonXmlRootElement(localName = "ProductDataList")
public class ProductDataList {
@JacksonXmlProperty(localName = "ProductData")
@JacksonXmlElementWrapper(useWrapping = false)
ProductData[] productDataArray = null;
public ProductData[] getProductDataArray() {
return productDataArray;
}
public void setProductDataArray(ProductData[] productDataArray) {
this.productDataArray = productDataArray;
}
}
Now the response looks like just as I wanted to be.
<ProductDataList>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
</ProductDataList>
Upvotes: 1
Reputation: 63
Objects aren't built that way. It's doing exactly as you're asking:
<ProductDataList> <!-- the ProductDataList object -->
<ProductData> <!-- the property containing the array -->
<ProductData>...</ProductData> <!-- each ProductData object -->
<ProductData>...</ProductData>
<ProductData>...</ProductData>
</ProductData>
</ProductDataList>
This is to ensure that other properties on the ProductDataList object would also have a spot inside the tags, e.g.
<ProductDataList>
<MyArray>...</MyArray>
<MyProperty></MyProperty>
</ProductDataList>
To get around this, you might as well try to cut out the Object middle-man.
@RequestMapping(value = "/productdata")
@ResponseBody
public ArrayList<ProductData> getProductDataList(@RequestParam final String[] ids) {
return productDataService.getProductData(ids);
}
If it works at all (I seem to remember JAXB not being able to parse ArrayLists), your ObjectMapper will give you...
<ArrayList>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
<ProductData>...</ProductData>
</ArrayList>
...instead of the root tag that you're hoping for. But if it DOES work, then just create a class that extends ArrayList and doesn't do anything, then return it instead.
public class ProductDataList<E> extends ArrayList<E>{ ... }
And then...
@RequestMapping(value = "/productdata")
@ResponseBody
public ProductDataList<ProductData> getProductDataList(@RequestParam final String[] ids) {
return (ProductDataList) productDataService.getProductData(ids);
}
Happy hunting.
Upvotes: 1