2014-09-22 3 views
1

Я пытаюсь распаковать следующий XML с помощью Джексона 2.4.0 XmlMapper:Джексон XML демаршаллинга и абстрактные классы

<root> 
    <a/> 
    <b/> 
</root> 

... и следующий POJO

class Root { 
    @JacksonXmlElementWrapper(useWrapping = false) 
    @JsonSubTypes({ 
     @JsonSubTypes.Type(name = "a", value = POJO_A.class), 
     @JsonSubTypes.Type(name = "b", value = POJO_B.class) 
    }) 
    public final List<AbstractPOJO> objects = new ArrayList<>(); 
} 

Я также пытался с JAXB @XmlElements аннотаций с тем же результатом, что:

Unrecognized field "a" (class Root), not marked as ignorable (1 known properties: "objects"]) 

Таким образом, кажется, что Ja ckson считает, что мой список называется «объекты» вместо «a» и «b». Обычно я исправляю это, используя @JsonProperty("newName"), но в этом случае я ожидал, что обработаю аннотации @JsonSubtypes или @XmlElements.

Как я могу изменить входной XML, есть ли что-нибудь еще, что я могу сделать, с комментариями Джексона, Джексона XML или JAXB?

Обновление: Забыл сказать, что проблема десериализации в той же коллекции (потому что мне нужно сохранить порядок, и они могут быть смешаны). Выполнение этого в отдельных полях работает отлично.

ответ

0

«Поскольку я не могу изменить входной XML, есть ли что-нибудь еще, что я могу сделать, с помощью комментариев Джексона, Джексона XML или JAXB?»

Я не очень хорошо знаком с функциями Xml от Jackson. Но с JAXB Unmarshaller и небольшим изменением аннотаций это может быть достигнуто.

Для вашего List<AbstractPojo> вы можете использовать @XmlAnyElement(lax = true).

С lax = true:

Если верно, когда элемент соответствует свойству помеченного XmlAnyElement известно JAXBContext (например, есть класс с XmlRootElement, который имеет такое же имя тега, или есть XmlElementDecl, что имеет то же самое имя тега), то unmarshaller охотно распаковать этот элемент объекта JAXB, а демаршаллизации его DOM

которые в основном означает, что если мы аннотирования PojoA и PojoB с @XmlRootElement и передать имя элемента в качестве атрибута name (@XmlRootElement(name = "a")), по определению это должно работать.

Давайте дадим ему шанс:

public abstract class AbstractPojo { 
    // note this class is not annotated. It will be known in the context 
    // as we're explicitly using the type int the Root class 
} 

@XmlRootElement(name = "a") 
public class PojoA extends AbstractPojo { 
} 

@XmlRootElement(name = "b") 
public class PojoB extends AbstractPojo { 
} 

@XmlAccessorType(XmlAccessType.FIELD) 
@XmlRootElement(name = "root") 
public class Root { 

    @XmlAnyElement(lax = true) 
    protected List<AbstractPojo> objects; 

    public List<AbstractPojo> getObjects() { 
     if (objects == null) { 
      objects = new ArrayList<>(); 
     } 
     return this.objects; 
    } 
} 

Используя следующий файл XML, чтобы проверить

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <a/> <b/> <b/> <a/> <a/> <b/> <b/> 
</root> 

И следующая тестовая программа

public class JaxbTest { 
    private static final String FILE_NAME = "test.xml"; 

    public static void main(String[] args) throws Exception { 
     JAXBContext context = JAXBContext.newInstance(Root.class); 
     Unmarshaller unmarshaller = context.createUnmarshaller(); 
     File f = new File(FILE_NAME); 
     Root root = (Root)unmarshaller.unmarshal(f); 
     List<AbstractPojo> list = root.getObjects(); 

     for (AbstractPojo p : list) { 
      System.out.print(p instanceof PojoA ? "a " : "b "); 
     } 

     System.out.println(); 
     Marshaller marshaller = context.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 
     marshaller.marshal(root, System.out); 
    } 
} 

Мы получаем результат мы находимся находясь в поиске.

a b b a a b b // вместе с содержимым XML-файла мы мобилизовывать для тестирования


Вот некоторые хорошие ресурсы:


UPDATE

Хорошо, я понимаю, почему вы получали ElementNSImpl. Это не работает для меня, я получаю объекты ElementNSImpl. Вы знаете, что происходит?

Да, я вижу, что происходит. Во-первых, я скомпилировал xsd с xjc, и он создал ObjectFactory для меня, который объявил элементы. Вот почему это работает для меня.

Если вы этого не сделаете, вы должны явно помещать PojoA и PojoB в контекст.

JAXBContext.newInstance(Root.class, PojoA.class, PojoB.class); 
+0

Это не работает для меня , Я получаю объекты ElementNSImpl. Вы знаете, что происходит? Я добавлю свое решение тем временем –

+0

Пожалуйста, см. Мое ** ОБНОВЛЕНИЕ **. Также вы не могли бы опубликовать, как это реализовано с помощью '@ XmlElements/@ XmlElementRefs'. Я не смог заставить его работать. –

1

Когда я пытался peeskillet ответа и изменил Джексон JAXB unmarshaller, используя @XmlElements/@XmlElementRefs работал (@XmlAnyElement не по какой-то причине, я получаю список ElementNSImpl вместо моих собственных классов).

Было бы хорошо, если бы это работало с Джексоном, но в то же время это способ сделать эту работу.

UPDATE:

Что я сделал:

class Root { 
    @XmlElements({ 
     @XmlElement(name = "a", type = PojoA.class), 
     @XmlElement(name = "b", type = PojoB.class) 
    }) 
    public final List<AbstractPOJO> objects = new ArrayList<>(); 
} 

Я также добавил @XmlRootElement(name = "a") к PojoA, но я думаю, что он не используется, когда у вас есть @XmlElements

+0

Да, это, вероятно, более семантически правильно с вашим доменом :-) +1 –

Смежные вопросы