2

I receive JSON documents from a third party tool, which is under development, i.e. the JSON is extended frequently. I would like to stick with the simplest deserialization mechanism in order to avoid increased maintenance that comes with custom deserializers.

Here's the relevant part of the JSON:

{
"key1": "value1",
"key2": "value2",
"variants": {
    "columns": ["chr", "pos", "id", "ref", "alt", "qual", "filter", "type", "genotype", "alignpos", "basepos", "signalpos"],
    "rows": [
        ["17", 19561093, ".", "G", "C", 51, "PASS", "SNV", "het.", 97, 147, 1761],
        ["17", 19561123, ".", "T", "G", 51, "PASS", "SNV", "het.", 127, 177, 2120],
        ["17", 19561149, ".", "G", "A", 51, "PASS", "SNV", "het.", 153, 203, 2432]],
    "xranges": [
        [829, 1129],
        [1480, 1780],
        [1492, 1792]],
}}

The issue is with the variants tag, specifically with the "rows". It looks as if "rows" were serialized with @JsonValue annotation in order to get rid of the field names that would otherwise be part of every "row". Instead an additional "columns" field was serialized to at least mention the column names once.

Without the variants tag, I've been successful deserializing the JSON into a POJO using ObjectMapper:

public class Pojo {
    private String key1;
    private String key2;
    public String getKey1() {
        return key1;
    }
    public void setKey1(String key1) {
        this.key1 = key1;
    }
    public String getKey2() {
        return key2;
    }
    public void setKey2(String key2) {
        this.key2 = key2;
    }
}

and

public static void main(String[] args) {
    final ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    Pojo pojo = objectMapper.readValue(jsonFile, Pojo.class);
}

Now, in order to deserialize the variants, I added this to my original POJO

@JsonProperty("variants")
private PojoVariants pojoVariants;

Then, implemented PojoVariants

public class PojoVariants {

    @JsonProperty("columns")
    private String[] columnNames;

    @JsonProperty("rows")
    private PojoVariant[] pojoVariantArr;

    public String[] getColumnNames() {
        return columnNames;
    }

    public void setColumnNames(String[] columnNames) {
        this.columnNames = columnNames;
    }

    public PojoVariant[] getPojoVariantArr() {
        return pojoVariantArr;
    }

    public void setPojoVariantArr(PojoVariant[] pojoVariantArr) {
        this.pojoVariantArr = pojoVariantArr;
    }

}

and finally PojoVariant

@JsonPropertyOrder({
        "chr", "pos",
        "id", "ref", "alt",
        "quality", "filter", "type", "genotype",
        "alignPos", "basePos", "signalPos" })
public class PojoVariant {

    private String chr;
    private int pos;

    private String id;
    private String ref;
    private String alt;

    private int quality;
    private String filter;
    private String type;
    private String genotype;

    private int alignPos;
    private int basePos;
    private int signalPos;

    @JsonCreator
    public PojoVariant(
            String chr, int pos,
            String id, String ref, String alt,
            int quality, String filter, String type, String genotype,
            int alignPos, int basePos, int signalPos) {

    }

}

Setting @JsonPropertyOrder, I hoped that Jackson would be able to figure out that I would like to read every "row" into a PojoVariant instance. However, using the original main method, I get this stack trace:

org.codehaus.jackson.map.JsonMappingException: Argument #0 of constructor [constructor for my.json.stuff.PojoVariant, annotations: {interface org.codehaus.jackson.annotate.JsonCreator=@org.codehaus.jackson.annotate.JsonCreator()}] has no property name annotation; must have name when multiple-paramater constructor annotated as Creator

I understand that Jackson would like me annotate the constructor arguments with property-names. But the JSON contains none to begin with. What am I missing? Or is this approach simply not supported - do I have to implement a custom deserializer?

Uwe
  • 85
  • 1
  • 5

1 Answers1

0

From jackson annotations javadoc, @JsonPropertyOrder is used for serialization, not deserialization:

Annotation that can be used to define ordering (possibly partial) to use when serializing object properties.

Also, when the constructor is annotated with @JsonCreator, constructor must either be:

  • Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator.
  • Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to.

The problem is that you try to deserialize from a json list ["17", 19561093, ".", ..] to a PojoVariant. Jackson is smart but not that smart. Here he needs some help and you can help him implementing a custom deserializer. We don't wanna do that, so let's hope we can find something else.

The hint comes from the @JsonCreator javadoc (first bullet, bold text). And, because Jackson knows how to bind the list into an array of objects, we can rewrite PojoVariant constructor like this:

@JsonCreator
public PojoVariant(Object[] params) {
    this.chr = (String) params[0];
    this.pos = (int) params[1];
    this.id = (String) params[2];
    this.ref = (String) params[3];
    this.alt = (String) params[4];
    this.quality = (int) params[5];
    this.filter = (String) params[6];
    this.type = (String) params[7];
    this.genotype = (String) params[8];
    this.alignPos = (int) params[9]; 
    this.basePos = (int) params[10];
    this.signalPos = (int) params[11];
}
Jorj
  • 1,291
  • 1
  • 11
  • 32