0

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>
2
  • 2
    Why you did not just return the ArrayList<ProductData> products? Commented Apr 12, 2016 at 18:14
  • @AliDehghani in fact I tried it. See the edit. Commented Apr 12, 2016 at 18:39

2 Answers 2

1

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.

  • Kieron
Sign up to request clarification or add additional context in comments.

2 Comments

When ArrayList<ProductData> is returned what I see is a list of <item>...</item> instead of <ProductData>...</ProductData> elements. See the edit.
Maybe try making "public class ProductDataList<ProductData> extends ArrayList<E> { /* blah */ }" -- maybe the typed <E> object cast in the class will make the XML ObjectMapper grab the classname instead of "item". If not, it seems like you may need to add some sort of custom ObjectMapper for this new ProductDataList object.
1

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>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.