Десериализуйте JSON с помощью Jackson и @JsonCreator

Я получаю документы JSON от стороннего инструмента, который находится в стадии разработки, т.е. JSON часто расширяется. Я хотел бы придерживаться простейшего механизма десериализации, чтобы избежать чрезмерного обслуживания, связанного с настраиваемыми десериализаторами.

Вот соответствующая часть 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]],
}}

Проблема связана с тегом вариантов, в частности с «строками». Похоже, что «строки» были сериализованы с аннотацией @JsonValue, чтобы избавиться от имен полей, которые в противном случае были бы частью каждой «строки». Вместо этого было сериализовано дополнительное поле «столбцы», чтобы хотя бы один раз упомянуть имена столбцов.

Без тега вариантов мне удалось десериализовать JSON в POJO с помощью 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;
    }
}

и

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);
}

Теперь, чтобы десериализовать варианты, я добавил это в свой исходный POJO

@JsonProperty("variants")
private PojoVariants pojoVariants;

Затем реализовал 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;
    }

}

и наконец 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) {

    }

}

Устанавливая @JsonPropertyOrder, я надеялся, что Джексон сможет понять, что я хотел бы читать каждую «строку» в экземпляре PojoVariant. Однако, используя исходный метод main, я получаю эту трассировку стека:

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

Я понимаю, что Джексон хотел бы, чтобы я аннотировал аргументы конструктора именами свойств. Но JSON изначально не содержит. Что мне не хватает? Или этот подход просто не поддерживается - мне нужно реализовать собственный десериализатор?


person Uwe    schedule 30.01.2019    source источник
comment
Я никогда не использовал Джексона, но он кажется излишне сложным. Используйте gson.   -  person Sedrick    schedule 30.01.2019


Ответы (1)


Из javadoc с аннотациями Джексона @JsonPropertyOrder используется для сериализации, а не десериализации:

Аннотация, которая может использоваться для определения порядка (возможно, частичного) для использования при сериализации свойств объекта.

Кроме того, когда конструктор помечен @JsonCreator, конструктор должен быть:

  • Конструктор с одним аргументом / фабричный метод без аннотации JsonProperty для аргумента: если это так, то это так называемый «создатель делегата», и в этом случае Джексон сначала связывает JSON с типом аргумента, а затем звонит автору.
  • Конструктор / фабричный метод, в котором каждый аргумент аннотируется JsonProperty или JacksonInject, чтобы указать имя свойства для привязки.

Проблема в том, что вы пытаетесь выполнить десериализацию из списка json ["17", 19561093, ".", ..] в PojoVariant. Джексон умен, но не настолько умен. Здесь ему нужна помощь, и вы можете помочь ему реализовать собственный десериализатор. Мы не хотим этого делать, поэтому будем надеяться, что сможем найти что-нибудь еще.

Подсказка взята из @JsonCreator javadoc (первый маркер, жирный текст). А поскольку Джексон знает, как связать список с массивом объектов, мы можем переписать конструктор PojoVariant следующим образом:

@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];
}
person Jorj    schedule 30.01.2019
comment
Отличный ответ. Хорошая деталь. Работает! Большое спасибо!! - person Uwe; 30.01.2019