如何解组包含在另一个元素中的元素

时间:2017-11-20 21:56:53

标签: java jaxb

我想解组下面的XML并从中仅提取CD元素的列表:

<?xml version="1.0" encoding="UTF-8"?>
<Root>
    <Header>
        <SomData>
            <Name>Name</Name>
        </SomData>
    </Header>
    <Message>
        <DziubasekInfo>
            <some_data>Data</some_data>
        </DziubasekInfo>
        <CATALOG>
            <Entry>
                <CD>
                    <TITLE>Empire Burlesque</TITLE>
                    <ARTIST>Bob Dylan</ARTIST>
                    <COUNTRY>USA</COUNTRY>
                    <COMPANY>Columbia</COMPANY>
                    <PRICE>10.90</PRICE>
                    <YEAR>1985</YEAR>
                </CD>
            </Entry>
            <Entry>
                <CD>
                    <TITLE>Hide your heart</TITLE>
                    <ARTIST>Bonnie Tyler</ARTIST>
                    <COUNTRY>UK</COUNTRY>
                    <COMPANY>CBS Records</COMPANY>
                    <PRICE>9.90</PRICE>
                    <YEAR>1988</YEAR>
                </CD>
            </Entry>
            <Entry>
                <CD>
                    <TITLE>Greatest Hits</TITLE>
                    <ARTIST>Dolly Parton</ARTIST>
                    <COUNTRY>USA</COUNTRY>
                    <COMPANY>RCA</COMPANY>
                    <PRICE>9.90</PRICE>
                    <YEAR>1982</YEAR>
                </CD>
            </Entry>
        </CATALOG>
    </Message>
</Root>

这是我的实际代码:

@XmlRootElement(name="CATALOG")
@XmlAccessorType(XmlAccessType.FIELD)
public class Catalog {


    @XmlElement(name = "Entry", type = Entry.class)
    private List<Entry> list = new ArrayList<>();

    .... getters, setters

}
@XmlType(name="Entry")
@XmlAccessorType(XmlAccessType.FIELD)
public class Entry {


    @XmlElement(name = "CD", type = Cd.class)
    private List<Cd> cdList = new ArrayList<>();

    ... getters, setters

}
@XmlType(name="CD")
@XmlAccessorType(XmlAccessType.FIELD)
public class Cd {

    @XmlElement(name="TITLE")
    private String title;

    @XmlElement(name="ARTIST")
    private String artist;

    @XmlElement(name="PRICE")
    private String price;

    .... getters, setters

}
InputStream inp = getClass().getResourceAsStream("cd_catalog.xml");

XMLInputFactory xif = XMLInputFactory.newFactory();
StreamSource source = new StreamSource(inp);

XMLStreamReader xsr = xif.createXMLStreamReader(source);

while (!(xsr.isStartElement() && "CATALOG".equals(xsr.getLocalName()))) {
    xsr.next();
}

JAXBContext jc = JAXBContext.newInstance(Catalog.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
Catalog catalog = unmarshaller.unmarshal(xsr, Catalog.class).getValue();

System.out.println("Entries = " + catalog.getList().size());

for (Entry e : catalog.getList()) {
    System.out.format("CD: %20s / %30s / %10s\n",
        e.getCdList().get(0).getArtist(), 
        e.getCdList().get(0).getTitle(), 
        e.getCdList().get(0).getPrice());
}

xsr.close();

正如您所看到的,这里有3个类:Catalog包裹Entry,后者又包裹Cd类。
实际上,每个Entry只包含一个Cd条目。

有没有办法消除这个包装类之一,只使用2个类而不是3个?
并且为了简化这段代码,特别是这个片段看起来有点尴尬:

e.getCdList().get(0).getArtist(), 
e.getCdList().get(0).getTitle(), 
e.getCdList().get(0).getPrice()

拥有一个只包含一个项目的列表并且总是只获得这个条目有点愚蠢,最好这样做:

e.getCdList().getArtist(), e.getCdList().getTitle(), e.getCdList().getPrice()

2 个答案:

答案 0 :(得分:0)

任何与XML相关的映射的基本点不是代码,而是XML。(实际上是XML Schema)。您的JAXB注释定义了架构。所以,如果你的XML有架构 就像你展示:

<CATALOG>
        <Entry>
            <CD>
                <TITLE>Empire Burlesque</TITLE>
                <ARTIST>Bob Dylan</ARTIST>
                <COUNTRY>USA</COUNTRY>
                <COMPANY>Columbia</COMPANY>
                <PRICE>10.90</PRICE>
                <YEAR>1985</YEAR>
            </CD>
        </Entry>
        ...

那就是它的本质。 Entry元素有CD元素。 如果您想要消除CD元素,您的XML必须如下所示:

<CATALOG>
        <Entry>
                <TITLE>Empire Burlesque</TITLE>
                <ARTIST>Bob Dylan</ARTIST>
                <COUNTRY>USA</COUNTRY>
                <COMPANY>Columbia</COMPANY>
                <PRICE>10.90</PRICE>
                <YEAR>1985</YEAR>
        </Entry>

不多也不少...... Java对象(使用JAXB注释)只是XML模式所指示的反映。如果你不能改变XML - 你必须忍受它。

BTW:我很确定Entry可能不含CD元素,如DVD,TAPE,VINIL等...

答案 1 :(得分:0)

我终于使用EclipseLink / Moxy和@XmlPath(value="Entry/CD")注释解决了这个问题。

Cd类与问题中的相同。
这是Catalog类:

@XmlRootElement(name="CATALOG")
@XmlAccessorType(XmlAccessType.FIELD)
public class Catalog {

   @XmlElement(name = "CD", type = Cd.class)
   @XmlPath(value="Entry/CD")
   private List<Cd> list = new ArrayList<>();

    ... getters/setters

}

以下是代码:

System.setProperty("javax.xml.bind.context.factory","org.eclipse.persistence.jaxb.JAXBContextFactory");

        InputStream inp = getClass().getResourceAsStream("cd_catalog.xml");

        assertTrue(inp != null);

        XMLInputFactory xif = XMLInputFactory.newFactory();
        StreamSource source = new StreamSource(inp);

        XMLStreamReader xsr = xif.createXMLStreamReader(source);

        while (!(xsr.isStartElement() && "CATALOG".equals(xsr.getLocalName()))) {
            xsr.next();
        }

        JAXBContext jc = JAXBContext.newInstance(Catalog.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Catalog catalog = unmarshaller.unmarshal(xsr, Catalog.class).getValue();

        System.out.println("Entries = " + catalog.getList().size());

        for (Cd e : catalog.getList()) {
            System.out.format("CD: %20s / %30s / %10s\n", e.getArtist(), e.getTitle(), e.getPrice());
        }

        xsr.close();