Titan247
Titan247

Reputation: 41

Jackson xml deserialization - serialize to a list with arbitrary elements in between

I am using Jackson to deserialize XML. It works fine when serializing into a list when there are no other elements in between, but if i insert some other field in the middle somewhere, it seems to only put the things below the field into my list. See below only the ProductA objects after the Product B tag are being included.

I set a breakpoint to get the JsonNode object using the xmlMapper readTree method, and the properties above the ProductB tag are not there in the node tree.

Is there a way using jackson (or another library) to get all ProductA elements inside the Warehouse element, independent of the ordering of the Warehouse child elements?

public class Warehouse {
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty(localName = "ProductA")
private List<ProductA> productAList;

@JacksonXmlProperty(localName = "ProductB")
private String productB;
//getters and setters

}
public class ProductA {
@JacksonXmlProperty(localName = "PropA")
private String propA;

//getters and setters
}
<Warehouse>
 <ProductA>
  <propA>abc</propA>
 </ProductA>
 <ProductA>
  <propA>abc</propA>
 </ProductA>
 <ProductB>def</ProductB>
 <ProductA>
  <propA>abc</propA>
 </ProductA>
</Warehouse>

Upvotes: 4

Views: 1354

Answers (1)

jnorman
jnorman

Reputation: 626

Change your Warehouse class to use a method just for setting the value of productAList from XML, and remove the annotations from the productAList property.

@JsonSetter(value =  "ProductA")
public void setProductAListFromXml(ProductA productA) {
    if (this.productAList == null) {
        this.productAList = new ArrayList<ProductA>();
    } 
    this.productAList.add(productA);
}

Here is the full Warehouse class:

@JacksonXmlRootElement(localName = "Warehouse")
public class Warehouse {
    private List<ProductA> productAList;

    @JacksonXmlProperty(localName = "ProductB")
    private String productB;

    public List<ProductA> getProductAList() {
        return productAList;
    }

    public void setProductAList(List<ProductA> productAList) {
        this.productAList = productAList;
    }

    @JsonSetter(value =  "ProductA")
    public void setProductAListFromXml(ProductA productA) {
        if (this.productAList == null) {
            this.productAList = new ArrayList<ProductA>();
        } 
        this.productAList.add(productA);
    }
    
    public String getProductB() {
        return productB;
    }
    
    public List<ProductA> getProductA() {
        return productAList;
    }
}

Using the class like this:

String str = "<Warehouse>\r\n" + 
                " <ProductA>\r\n" + 
                "  <propA>abc</propA>\r\n" + 
                " </ProductA>\r\n" + 
                " <ProductA>\r\n" + 
                "  <propA>abc</propA>\r\n" + 
                " </ProductA>\r\n" + 
                " <ProductB>def</ProductB>\r\n" + 
                " <ProductA>\r\n" + 
                "  <propA>abc</propA>\r\n" + 
                " </ProductA>\r\n" + 
                "</Warehouse>";
        
XmlMapper mapper = new XmlMapper();
Warehouse warehouse = mapper.readValue(str, Warehouse.class);
System.out.println(warehouse.getProductB());
System.out.println(warehouse.getProductA());

Produces this output:

def
[ProductA [propA=abc], ProductA [propA=abc], ProductA [propA=abc]]

Upvotes: 3

Related Questions