ravindrab
ravindrab

Reputation: 2792

Spring boot REST API Returning a List/Array Formatting issue

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

Answers (2)

ravindrab
ravindrab

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

Kieron Alsmith
Kieron Alsmith

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.

  • Kieron

Upvotes: 1

Related Questions