Невозможно десериализовать (преобразовать) развернутый список, когда он второй в классе, используя Jackson XmlWrapper - PullRequest
0 голосов
/ 08 февраля 2019

Я пытаюсь использовать XmlMapper от Джексона для десериализации некоторых простых XML-файлов, содержащих развернутые списки.

Мой код:

package zm.study.xmlserialize.jackson;

import java.util.List;

import org.junit.Test;

import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper    ;

public class JacksonListTest {

    public static class A {
        public String c;
        @JacksonXmlElementWrapper(useWrapping=false)
        public List<String> as;
    }

    @Test
    public void deserializeTest() throws Exception
    {
        XmlMapper mapper = new XmlMapper();
        String xml = "<A><c>c</c><as>a1</as><as>a2</as></A>";
        //mapper.readValue(xml, A.class);
        mapper.convertValue(mapper.readTree(xml), A.class);
    }

}

К сожалению, библиотека вызывает исключение, когда это происходит, когдаlist не первый в классе / xml.

Исключение исчезает, когда я удаляю элемент "c" из xml и класса.Исключение также исчезает, если я использую readValue вместо convertValue, но мне нужен для работы convertMethod.

Исключение составляет:

com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList` out of VALUE_STRING token
 at [Source: (StringReader); line: 1, column: 18] (through reference chain: zm.study.xmlserialize.jackson.JacksonListTest$A["as"])
    at com.fasterxml.jackson.databind.exc.MismatchedInputException.from(MismatchedInputException.java:63)
    at com.fasterxml.jackson.databind.DeserializationContext.reportInputMismatch(DeserializationContext.java:1343)

...

1 Ответ

0 голосов
/ 09 февраля 2019

Я не уверен, что это можно сделать таким образом.Метод readTree возвращает объект, который расширяет JsonNode, в этом случае это будет ObjectNode.ObjectNode не принимает два свойства с одинаковыми именами и, наконец, после десериализации представляет:

{"c":"c","as":"a2"}

После этого вы хотите преобразовать этот узел в класс A POJO.По умолчанию десериализатор для List ожидает токен START_ARRAY, а не String.Вы можете заставить его работать, реализуя пользовательский конвертер, который расширяет StdConverter<String, List>, но список будет урезан до одного элемента.В этом случае, я думаю, вы должны использовать readValue метод, потому что вам нужно указать Jackson, что as элементы - это развернутый массив.

EDIT
После того, как выкомментарий Я понял, что мы можем обмануть XmlMapper, чтобы использовать то, что мы хотим.Совершенно очевидно, что Jackson использует JsonNodeDeserializer для десериализации JsonNode -s.Итак, все, что нам нужно сделать, это найти место, где мы можем внедрить наш код.К счастью, есть метод _handleDuplicateField, который обрабатывает наш случай.По умолчанию генерируется исключение, если установлен флаг FAIL_ON_READING_DUP_TREE_KEY:

protected void _handleDuplicateField(JsonParser p, DeserializationContext ctxt,
        JsonNodeFactory nodeFactory,
        String fieldName, ObjectNode objectNode,
        JsonNode oldValue, JsonNode newValue)
    throws JsonProcessingException
{
    // [databind#237]: Report an error if asked to do so:
    if (ctxt.isEnabled(DeserializationFeature.FAIL_ON_READING_DUP_TREE_KEY)) {
        ctxt.reportInputMismatch(JsonNode.class,
                "Duplicate field '%s' for ObjectNode: not allowed when FAIL_ON_READING_DUP_TREE_KEY enabled",
                fieldName);
    }
}

Итак, давайте воспользуемся этим фактом и расширим этот класс:

class MergeDuplicateFieldsJsonNodeDeserializer extends JsonNodeDeserializer {
    @Override
    protected void _handleDuplicateField(JsonParser p, DeserializationContext ctxt, 
                                         JsonNodeFactory nodeFactory, String fieldName, ObjectNode objectNode, 
                                         JsonNode oldValue, JsonNode newValue) throws JsonProcessingException {
        super._handleDuplicateField(p, ctxt, nodeFactory, fieldName, objectNode, oldValue, newValue);

        ArrayNode array;
        if (oldValue instanceof ArrayNode) {
            // Merge 3-rd, 4-th, ..., n-th element to already existed array
            array = (ArrayNode) oldValue;
            array.add(newValue);
        } else {
            // Merge first two elements
            array = nodeFactory.arrayNode();
            array.add(oldValue);
            array.add(newValue);
        }
        objectNode.set(fieldName, array);
    }
}

Теперь нам нужно зарегистрировать этот десериализатор,Весь тест может выглядеть следующим образом:

import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.JsonNodeDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;

import java.util.Arrays;
import java.util.List;

public class XmlMapperApp {

    public static void main(String[] args) throws Exception {
        A a = new A();
        a.c = "String";
        a.as = Arrays.asList("1", "2", "tom", "Nick");

        SimpleModule mergeDuplicatesModule = new SimpleModule("Merge duplicated fields in array");
        mergeDuplicatesModule.addDeserializer(JsonNode.class, new MergeDuplicateFieldsJsonNodeDeserializer());

        XmlMapper mapper = new XmlMapper();
        mapper.registerModule(mergeDuplicatesModule);

        String xml = mapper.writeValueAsString(a);

        System.out.println(xml);
        System.out.println(mapper.readTree(xml));
    }
}

Над отпечатками кода:

<A><c>String</c><as>1</as><as>2</as><as>tom</as><as>Nick</as></A>
{"c":"String","as":["1","2","tom","Nick"]}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...