Jackson insists on mapping an unknown property

Viewed 1084

All, I'm using Jackson 2.9.5

I'm trying to parse the XML output of the final-stats module of Blazemeter Taurus.

There is an XML element "Group" with children "perc," for percentile. Perc has a "name" child that is unmapped. I believe the "name" child should be ignored because if I remove @JsonIgnorePropertues(ignoreUnknown=true), Jackson blows up because it doesn't recognize "name."

However when deserializing Group, name is not ignored. Instead I'm getting

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of com.mycompany.myproject.Percentile (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('perc/90.0') at [Source: (BufferedInputStream); line: 4, column: 20] (through reference chain: com.mycompany.myproject.Group["perc"]->java.util.ArrayList[0])

"perc/90.0" is the value of the unmapped "name" element.

What's even weirder is that when I try to deserialize only the perc element, it works fine.

This is the XML that fails:

<?xml version='1.0' encoding='UTF-8'?>
<Group label="https://myhost.mycompany.com:8443/login?from=%2F">
  <perc value="0.19300" param="90.0">
    <name>perc/90.0</name>
  </perc>
</Group

This is my deserialization code:

XmlMapper mapper = new XmlMapper();
mapper.registerModule(new JaxbAnnotationModule());
InputStream in =  Thread.currentThread().getContextClassLoader().getResourceAsStream("taurus/group-small.xml");
Group group = mapper.readValue(in, Group.class);

This XML works:

<perc value="0.19300" param="90.0">
  <name>perc/90.0</name>
</perc>

With this deserialization code:

XmlMapper mapper = new XmlMapper();
mapper.registerModule(new JaxbAnnotationModule());
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("taurus/percentile2.xml");
Percentile p = mapper.readValue(in, Percentile.class);

These are my mapping classes:

package com.mycompany.myproject;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name="perc")
public class Percentile {

    @XmlAttribute(name="value")
    private double value;

    @XmlAttribute(name="param")
    private double name;

    public double getValue() {
        return value;
    }

    public void setValue(double value) {
        this.value = value;
    }

    public double getName() {
        return name;
    }

    public void setName(double name) {
        this.name = name;
    }
}

package com.mycompany.myproject;

import java.util.List;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name="Group")
public class Group {

    @XmlAttribute(name="label")
    private String label;
    @XmlElement(name="perc")
    private List<Percentile> responseTimePercentiles;

    public String getLabel() {
        return label;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public List<Percentile> getResponseTimePercentiles() {
        return responseTimePercentiles;
    }

    public void setResponseTimePercentiles(List<Percentile> responseTimePercentiles) {
        this.responseTimePercentiles = responseTimePercentiles;
    }
}

Thanks

2 Answers

The issue here is that your Group class has defined a list of percentiles that map to an xml list element named <perc>. Your Group xml does not contain an xml list element called <perc>, but has the <perc> elements directly below the root xml.

Because @XmlElement(name="perc") is applied to the list field and @XmlRootElement(name="perc") is also applied to the individual percentile xml elements, your Group mapping is expecting an xml structure similar to:

<group>
  <perc/>
      <perc/> 
      <perc/>
      <perc/> 
  <perc/> 
<group>

The actual XML structure is:

<group>
  <perc/> 
  <perc/>
  <perc/> 
<group>

To get this working you have 2 options:

  1. Change the XML so it there is a containing list element (e.g <percentiles>) and change the annotation on the list field to be @XmlElement(name="percentiles").

  2. Accommodate the fact that you don't have an xml element that denotes the list in the mapping. To do this you need to:

    Add the following method to your Group mapper:

    @JsonSetter(value = "perc")
    public void setPercentile(Percentile percetile) {
       this.responseTimePercentiles.add(percetile);
    }
    

    Remove the @XmlElement(name="perc") annotation from list field in the Group class.

The net effect of this is that the mapping does not expect to find a containing list element in the XML, and each time it comes across a <perc> element it simply adds it to the list.

As a workaround does the alternative syntax work, specifying things to ignore, e.g.:

@JsonIgnoreProperties({ "internalId", "secretKey" })
Related