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

Java 如何使用JAX-B从XML中读取所选类的对象(多态),java,list,jaxb,polymorphism,Java,List,Jaxb,Polymorphism,我创建了一个多态对象列表。我可以将它们转换为XML,反之亦然(marshall和unmarshal使用JAXB),但我只想读取所选类中的对象 我使用了一些关于JAXB转换对象列表的教程,例如: 或者这个: 我有动物列表,还有两个扩展抽象动物类的类,狗,猫和鸟。我将该列表(包含狗和猫)保存为XML,并且我只想从XML文件中获取猫。有可能吗 使用教程中的代码-> 我会感到惊讶: Root{anives=[com.memorynotfound.xml.jaxb。Dog@520a3426,com.mem

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

我使用了一些关于JAXB转换对象列表的教程,例如: 或者这个: 我有动物列表,还有两个扩展抽象动物类的类,狗,猫和鸟。我将该列表(包含狗和猫)保存为XML,并且我只想从XML文件中获取猫。有可能吗

使用教程中的代码->

我会感到惊讶:

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

我努力实现的目标是:

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

在其中一个答案中,我了解了XMLEvent。这是一个非常好的想法,但几乎每个教程都提到了过滤。我想要达到的是摆脱所有其他类。 我试图更改代码:

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;
}

但是它不起作用,所以我不确定我是否理解这个解决方案。

因为在根类中,动物同时将dog和cat作为元素,它将尝试将其封送到这两个对象(如果可用)

实现所需功能的一种方法是删除XmlElementWrapper下的一个元素

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

您可以创建一个自定义的
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;
    }
}
然后,
Dog
Birds
元素将不会被读取

请注意,如果此实现嵌套了多个过滤元素,则可能会出现问题


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

但在这种情况下,我将无法读取狗。我想从所选的类中读取对象,所以只有猫(或者狗,如果用户选择狗类的话)。使用您的解决方案,我将能够只阅读猫,这是真的。但如果用户改变主意,我将无法只阅读狗。首先,感谢您的回复。我读过关于XMLEvent的文章,它看起来是一个非常好的过滤方法。但是如果我有狗,猫和鸟,我只想读猫呢?要过滤除CAT以外的所有类吗?如果(e.getEventType()==XMLEvent.START_元素&&(“Dog.”等于(e.asStartElement().getName().getLocalPart())| |“Bird.”等于(e.asStartElement().getName().getLocalPart())不起作用。如果我尝试不同的方法,但我想不出如何更改代码。我在答案中编写的
FilterDogXmlEventReader
是虚拟的。您可以通过添加一个包含所有筛选值的实例变量来改进它。然后在构建事件读取器时设置筛选值。然后在ll筛选值,而不是只匹配
Dog
谢谢,这正是我想要实现的。我需要仔细阅读您是如何做到这一点的,以进一步了解它,但谢谢!我认为您在布尔运算符优先级方面存在问题。我已更新了解决方案,以支持多个筛选值。
Root{animals=[ com.memorynotfound.xml.jaxb.Cat@18eed359]}
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;
    }
}
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"));