разобрать рекурсивное вложенное поле с помощью jackson

У меня есть JSON ниже, из которого мне нужно сделать Map<String, String>. В приведенном ниже JSON у меня есть только три уровня parentCategory, но в целом их может быть больше, а иногда и меньше.

  • Ключ моей карты будет 79720, который равен id раздела categories, а значение должно быть 10987, что равно id последнего parentCategory.

Теперь я могу вложить в них parentCategory, поэтому у меня возникают проблемы с созданием POJO для этого. У меня может быть один уровень parentCategory или я могу вложить parentCategory в каждый parentCategory, и мне всегда нужно получить идентификатор для последнего parentCategory.

Поэтому я создаю для этого POJO, перейдя на jsonschema2pojo и предоставив свой JSON, где он сгенерировал мне все файлы что мне нужно. С помощью приведенного ниже JSON было создано три класса, таких как ParentCategory, ParentCategory_ и ParentCategory__ для поля parentCategory. Теперь, поскольку я не знаю, сколько у меня parentCategory уровней, я не уверен, как лучше всего сгенерировать для них POJO и извлечь поле id для последних parentCategory. Есть ли способ сделать это с помощью Джексона?

Я должен прочитать все эти данные в POJO, так как я также читаю все другие поля.

{
    "paginationResponse": {
        "pageNumber": 1,
        "entriesPerPage": 200,
        "totalPages": 3
    },
    "listings": [
        {
            "categories": [
                {
                    "id": "79720",
                    "name": "Sunglasses",
                    "localizedName": "Sunglasses",
                    "level": 4,
                    "leafCategory": true,
                    "parentCategory": {
                        "id": "394",
                        "name": "Sunglasses & Fashion Eyewear",
                        "localizedName": "Sunglasses & Fashion Eyewear",
                        "level": 3,
                        "leafCategory": false,
                        "parentCategory": {
                            "id": "2340",
                            "name": "Men's Accessories",
                            "localizedName": "Men's Accessories",
                            "level": 2,
                            "leafCategory": false,
                            "parentCategory": {
                                "id": "10987",
                                "name": "Clothing, Shoes & Accessories",
                                "localizedName": "Clothing, Shoes & Accessories",
                                "level": 1,
                                "leafCategory": false
                            }
                        }
                    }
                }
            ],
            "processlisting": {
                ....
            },
            "processlistingmetainfo": {
                ...
            },
            "processlistingproperties": [
                {
                    "propertyName": "item_url",
                    "propertyValues": [
                        {
                            "stringValue": "url"
                        }
                    ]
                },
                {
                    "propertyName": "listing_site_id",
                    "propertyValues": [
                        {
                            "stringValue": "0"
                        }
                    ]
                }
            ]
        }
    ],
    "total": 100
}

Примечание. У меня нет контроля над этой структурой JSON, поскольку она нам не принадлежит, поэтому я не могу вообще изменить структуру JSON.

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

public class ParentCategory {
    @JsonProperty("id")
    private String id;
    @JsonProperty("name")
    private String name;
    @JsonProperty("localizedName")
    private String localizedName;
    @JsonProperty("level")
    private long level;
    @JsonProperty("leafCategory")
    private boolean leafCategory;
    @JsonProperty("parentCategory")
    private ParentCategory_ parentCategory;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    ....

}

public class ParentCategory_ {
    @JsonProperty("id")
    private String id;
    @JsonProperty("name")
    private String name;
    @JsonProperty("localizedName")
    private String localizedName;
    @JsonProperty("level")
    private long level;
    @JsonProperty("leafCategory")
    private boolean leafCategory;
    @JsonProperty("parentCategory")
    private ParentCategory__ parentCategory;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    ...

}


public class ParentCategory__ {
    @JsonProperty("id")
    private String id;
    @JsonProperty("name")
    private String name;
    @JsonProperty("localizedName")
    private String localizedName;
    @JsonProperty("level")
    private long level;
    @JsonProperty("leafCategory")
    private boolean leafCategory;
    @JsonIgnore
    private Map<String, Object> additionalProperties = new HashMap<String, Object>();

    ...

}

person user1950349    schedule 24.01.2018    source источник
comment
Мне кажется, вам нужно лучшее сопоставление JSON. Вместо жесткого кодирования идентичных рекурсивных объектов создайте древовидное отображение, где каждый уровень представлен одним и тем же классом с необязательным следующим родителем. Таким образом, вы можете работать с вашим деревом, как с обычным деревом, используя рекурсивные обходы и преобразования.   -  person SimY4    schedule 27.01.2018


Ответы (1)


Вообще говоря. Лучшим инструментом для получения определенных свойств JSON является Json-Path, который обеспечивает богатый язык запросов для поиска в дереве JSON.

Что касается вопроса, требуется получить два несвязанных свойства, поэтому требуется два сканирования дерева JSON. В вопросе не указано, как читается ввод, поэтому, возможно, можно прочитать весь входной поток в одну строку.

Ниже приведен код с запросами, необходимыми для получения обоих свойств. Это будет работать независимо от количества parentCategory уровней. Хитрость в том, что последний объект — единственный, у которого нет дочернего элемента parentCategory.

Я добавил комментарии к коду, поясняющие текст запроса.

    String categoryIdJsonPath = 
        "$" +              // start from tree root
        ".listings[0]" +   // get listings array's first (only) object
        ".categories[0]" + // get categories array's first (only) object
        ".id";             // get id property
    String lastParentIdJsonPath = 
        "$" +                         // start from tree root
        ".listings[0]" +              // get listings array's first (only) object
        ".categories[0]" +            // get categories array's first (only) object
        "..parentCategory" +          // do deep scan for all nested parentCategory objects 
        "[?(!(@.parentCategory))]" +  // filter by the object that does NOT have parentCategory property
        ".id";                        // get id property
    try {
        // read the whole input so it can be scanned twice
        String jsonInput = new String(Files.readAllBytes(Paths.get("C://temp/test.json")), Charset.forName("UTF-8"));
        String categoryId = JsonPath.read(jsonInput, categoryIdJsonPath);
        System.out.println(categoryId);
        // return type is always List when deep scan is requested
        List<String> lastParent = JsonPath.read(jsonInput, lastParentIdJsonPath);
        System.out.println(lastParent.get(0));
    } catch (Exception e) {
        e.printStackTrace();
    }
person Sharon Ben Asher    schedule 25.01.2018
comment
Я делаю HTTP-вызов и читаю все данные JSON как POJO, а затем использую этот POJO для извлечения всего. Итак, я подумал, есть ли способ прочитать это как POJO, а затем извлечь последний идентификатор parentCategory? Я могу легко извлечь 79720, который является идентификатором категории, но мой POJO вообще не работает, чтобы извлечь последний идентификатор parentCategory. - person user1950349; 25.01.2018
comment
но зачем считывать все данные в POJO, когда все, что вам нужно, это два свойства? прочитайте ответ HTTP в String и используйте решение, которое я дал. - person Sharon Ben Asher; 25.01.2018
comment
На самом деле я читаю много полей, а не только эти два, поэтому я создал POJO. И я застрял, когда мне нужно было извлечь последний идентификатор parentCategory - person user1950349; 25.01.2018
comment
тогда вы должны упомянуть об этом в вопросе. этот ответ лучше всего подходит для того, что здесь спрашивают. если вам нужно прочитать много полей, то прочитайте json в Map (Джексон может это сделать), а затем запросите записи карты - person Sharon Ben Asher; 26.01.2018
comment
Я нашел одну ошибку в вашем предложении во время тестирования. В моем случае listings - это массив, поэтому он может иметь несколько listing json object, а не только один. В вопросе я просто привел пример, чтобы упростить его, иначе вопрос стал бы большим. Это означает, что для каждого объекта списка у меня будет другой идентификатор категории и другой последний идентификатор категории partentCategory. Можете ли вы помочь мне, как решить эту проблему? У меня уже есть объект List<Listing>. - person user1950349; 27.01.2018