javaarraysjacksondeserializationfasterxml

Jackson xml deserializing inline array that is not contiguous


I have some strange xml I am trying deserialize to java objects

<Operator>
    <Parameter Key="a" Value="1"/>
    <Parameter Key="b" Value="2"/>
    <Parameter Key="c" Value="3"/>
    <StorageParameters Key="x" Value="***"/>
    <Parameter Key="d" Value="4"/>
</Operator>

I need to collect only the Parameter nodes as a list. I have written my java classes as follows

@JsonIgnoreProperties(ignoreUnknown = true)
public class Operator {
  
    @JacksonXmlProperty(localName="Parameter")
    @JacksonXmlElementWrapper(useWrapping = false)
    private List<Parameter> parameters;

   public Operator() {
       this.parameters = List.of();
   }

   public List<Parameter> getParameters() {
        return parameters;
    }

    public void setParameters(List<Parameter> parameters) {
        this.parameters = parameters;
    }
}


public class Parameter {
    private String Key;
    private String Value;

    public Parameter() {
        Key = "";
        Value = " ";
    }

    public String getKey() {
        return  Key;
    }

    public void setKey(String key) {
        Key = key;
    }

    public String getValue() { return Value; }

    public void setValue(String value) {
        Value = value;
    }
}

// Driver

JacksonXmlModule module = new JacksonXmlModule();
XmlMapper mapper = new XmlMapper(module);

Operator bean = mapper.readValue(xmlText, classOf[Operator])

When I run this the only value returned by the parameters list is the last Parameter entry with Key="d" and Value="4".

Is this expected behavior? Is there any annotation I can use to get all the Parameter Key/Value pairs?

Thanks!


Solution

  • The problem here is the presence of the <StorageParameters Key="x" Value="***"/> tag: the jackson library expects in a list to deserialize only the presence of n <Parameter/> equal tags but instead finds a different tag and consequently decides to interpret the list as distinct properties, so the last <Parameter Key="d" Value="4"/> overwrites the previous values of the Parameter property and then the strange behaviour (the other StorageParameters is ignored cause the JsonIgnoreProperties(ignoreUnknown = true) annotation in the Operator class).

    To avoid this behaviour you can delete the undesired StorageParameters and deserialize your xml parsing the old xml and creating a new one without the undesired tag like below :

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(new FileInputStream(xml));
    Node node = doc.getElementsByTagName("StorageParameters").item(0);
    node.getParentNode().removeChild(node);
    TransformerFactory tf = TransformerFactory.newInstance();
    Transformer t = tf.newTransformer();
    StringWriter writer = new StringWriter();
    t.transform(new DOMSource(doc), new StreamResult(writer));
    String result = writer.getBuffer().toString();
    Operator value = mapper.readValue(result, Operator.class);
    

    You have to use the JacksonXmlProperty(isAttribute = true) annotation in your Parameter to indicate that values are attributes:

    @Data
    public class Parameter {
    
        @JacksonXmlProperty(isAttribute = true)
        private String Key;
        @JacksonXmlProperty(isAttribute = true)
        private String Value;
    }
    
    @Data
    public class Operator {
    
        @JacksonXmlProperty(localName = "Parameter")
        @JacksonXmlElementWrapper(useWrapping = false)
        private List<Parameter> parameters;
    }