如何使用 JAX-B 从 XML 中读取所选 class 的对象(多态)

How to read objects (Polymorphic) of the chosen class, from the XML using JAX-B

我创建了一个多态对象列表。我可以将它们转换为 XML ,反之亦然(使用 JAXB 编组和解组。)但我只想读取所选 class.

中的对象

我使用了一些关于通过 JAXB 转换对象列表的教程,例如这个: http://memorynotfound.com/convert-xml-to-polymorphic-object-using-jax-b/ or this one: http://bdoughan.blogspot.com/2010/11/jaxb-and-inheritance-using-substitution.html 我有动物列表,还有两个扩展抽象动物 class、狗、猫和鸟的 classes。我将该列表(包含狗和猫)保存到 XML,并且我只想从 XML 文件中获取猫。可以制作吗?

使用教程中的代码 -> http://memorynotfound.com/convert-xml-to-polymorphic-object-using-jax-b/

    JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();

    Root root = (Root)unmarshaller.unmarshal(new StringReader(xml));

我会得到输出:

Root{animals=[com.memorynotfound.xml.jaxb.Dog@520a3426, com.memorynotfound.xml.jaxb.Cat@18eed359,com.memorynotfound.xml.jaxb.Bird@643bd123]}

我努力实现的是:

Root{animals=[ com.memorynotfound.xml.jaxb.Cat@18eed359]}

在其中一个答案中,我了解到 XMLEvent。这是一个非常好的主意,但几乎每个教程都谈到过滤。我想要实现的是摆脱所有其他 classes。 我尝试更改代码:

public XMLEvent nextEvent() throws XMLStreamException {
    // Read next event
    XMLEvent e = super.nextEvent();
    // If it's a start element for dog
    if (e.getEventType() == XMLEvent.START_ELEMENT &&  ("Cat".equals(e.asStartElement().getName().getLocalPart()) ||
            "Bird".equals(e.asStartElement().getName().getLocalPart()))
    )
    {

        // Then run through events until a closing dog event
        do {
            e = super.nextEvent();
        } while (e.getEventType() != XMLEvent.END_ELEMENT || ! ("Cat".equals(e.asStartElement().getName().getLocalPart()) ||
                "Bird".equals(e.asStartElement().getName().getLocalPart()))

        );
        // then read next event after the  ends
        e = super.nextEvent();
    }
    return e;
}

但是没有用,所以我不确定我是否理解解决方案。

由于在根中 class animals 将 dog 和 cat 作为元素,如果可用,它会尝试将其编组到这两个对象。

一种实现您想要的方法是删除 XmlElementWrapper 下的一个元素。

  @XmlElementWrapper(name = "animals")
  @XmlElements({
    @XmlElement(name = "cat", type = Cat.class)
  })
  public void setAnimals(List<Animal> animals) {
    this.animals = animals;
  }

以上会给你:

Root{animals=[ com.memorynotfound.xml.jaxb.Cat@18eed359]}

您可以创建自定义 javax.xml.stream.XMLEventReader 并过滤一些事件。这是一个愚蠢但有效的例子:

public final class FilteredXmlEventReader extends EventReaderDelegate {

    final Set<String> filteredElements;

    FilteredXmlEventReader(XMLEventReader delegate, String... filteredElements) {
        super(delegate);
        this.filteredElements = new HashSet<String>(Arrays.asList(filteredElements));
    }

    public XMLEvent nextEvent() throws XMLStreamException {
        // Read next event

        XMLEvent e = super.nextEvent();
        // If it's a start element for any filtered
        if (e.getEventType() == XMLEvent.START_ELEMENT && filteredElements.contains(e.asStartElement().getName().getLocalPart())) {
            String element = e.asStartElement().getName().getLocalPart();
            // Then run through events until a closing similar element
            do {
                e = super.nextEvent();
            } while (e.getEventType() != XMLEvent.END_ELEMENT || !element.equals(e.asEndElement().getName().getLocalPart()));
            // then read next tag after closing element
            e = super.nextEvent();
        }
        return e;
    }
}

然后使用此 XMLEventReader:

解组您的内容
JAXBContext jaxbContext = JAXBContext.newInstance(Root.class);
XMLEventReader xmlReader = XMLInputFactory.newFactory().createXMLEventReader(new StringReader(xml));
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
Root root = (Root) unmarshaller.unmarshal(new FilteredXmlEventReader(xmlReader, "Dog", "Bird"));

那么DogBirds元素将不会被读取。

请注意,如果多个筛选元素嵌套在此实现中,可能会出现问题。

编辑:编辑上面的代码以支持多个过滤元素。