У меня есть документ XML
, который я хочу десериализовать в объект класса, который я не знаю во время компиляции, и тип определяется как именем корневого элемента документа, так и текстовым содержимым элемента type
,Проблема двоякая:
У меня есть списки элементов в моем документе XML
, поэтому я не могу разобрать его в JsonNode
/ Map
, потому что в лучшем случае это будеттолько предоставьте мне последнее значение в списке, информация теряется в процессе.Все решения, которые я нашел, основаны на JsonNode
API
(например, convertValue
или treeToValue
).
Я не могу использовать JsonTypeInfo
- я не знаюконкретные классы, которые будут deserialised
, и поэтому я не могу использовать аннотации.Кроме того, я не думаю, что JsonTypeInfo
является настолько гибким, чтобы включать как имя корневого элемента, так и значение свойства.
Я попытался использовать пользовательский десериализатор, который обернул бы фактический проанализированный JsonNode
в другой Object
с rootElement в качестве единственного ключа (по умолчанию этот корневой элемент не включен в анализируемый документ).После этого я десериализирую документ, получаю корневой элемент из узла, затем беру значение элемента типа, объединяю их, создаю имя класса и затем преобразовываю JsonNode
в конечный объект.Проблема с этим подходом состоит в том, что он плохо обрабатывает списки и либо выдает и исключает недопустимое отображение, либо предоставляет мне последний элемент списка в зависимости от того, как я пишу свою модель или настраиваю маппер.package zm.study.xmlserialize.jackson;
import static org.junit.Assert.assertEquals;
import java.io.StringReader;
import java.util.List;
import org.junit.Test;
import com.fasterxml.jackson.core.JsonParser;
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.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser;
public class JacksonListTest2 {
public static class A < D extends AData > {
public String type;
public D data;
}
public static class AData {}
public static class BRequestData extends AData {
@JacksonXmlElementWrapper(useWrapping = false)
public List < String > bs;
}
public static class BRequest extends A {}
@Test
public void test() throws Exception {
String xml;
xml = "<Request><type>B</type><data><bs>1</bs><bs>2</bs></data></Request>";
XmlMapper mapper = new XmlMapper();
mapper.registerModule(
new SimpleModule()
.addDeserializer(JsonNode.class, new JsonNodeDeserializer() {
@Override
public JsonNode deserialize(JsonParser p, DeserializationContext ctxt) throws java.io.IOException {
String rootName = ((FromXmlParser) p).getStaxReader().getLocalName();
return ctxt.getNodeFactory().objectNode().set(rootName, super.deserialize(p, ctxt));
};
})
);
JsonNode rootNode = mapper.readTree(new StringReader(xml));
String rootName = rootNode.fields().next().getKey();
JsonNode contentNode = rootNode.get(rootName);
String type = contentNode.get("type").asText();
String className = getClass().getCanonicalName() + "$" + type + rootName;
BRequest value = (BRequest) mapper.convertValue(contentNode, Class.forName(className));
System.out.println(value.data.bs);
assertEquals(2, value.data.bs.size());
}
}
Мой подход приводит к исключению:
Невозможно десериализовать экземпляр java.util.ArrayList
из токена VALUE_STRING
Я также попытался создать пользовательский десериализатор, разбирающий достаточно документа, чтобы выяснить, с каким типом я имею дело, и затем делегировать оставшуюся часть фактического анализа (элемент данных) десериализатору по умолчанию, но мне не удалосьнаписать какой-нибудь разумный код, чтобы сделать это.
Я думаю, мне нужен новый подход, есть идеи?