JAXB将多个XML元素反序列化为单个类

8

我有以下XML结构,它在多个XML元素中建模单个概念。这种格式不在我的控制范围内。

<Output>
  <Wrapper>
    <Channel>
      <id>1</id>
      <type>x</type>
    </Channel>
    <Channel>
      <id>2</id>
      <type>y</type>
    </Channel>
    <ChannelName>
      <id>1</id>
      <name>Channel name</name>
    </ChannelName>
    <ChannelName>
      <id>2</id>
      <name>Another channel name</name>
    </ChannelName>
  </Wrapper>
</Output>

我希望在我有控制权且可以拥有更简单的Channel表的数据库中对此进行建模,该表具有idtypename字段。因此,我想将其反序列化为Wrapper类上的单个List<Channel>
这可以通过@Xml...注释自动完成吗?我目前正在使用JAXB将其反序列化为单独的@XmlElement(name="Channel")@XmlElement(name="ChannelName")类列表,然后在Channel上后处理瞬态ChannelName/name,但我认为一定有更简单的自动映射这些元素的方法。或者这是XSLT的工作?
知道XML作为HTTP文件POST文件传入,并且我正在使用Spring 3、Java和Hibernate可能会有所帮助。我希望EclipseLink JAXB (MOXy)中的某些内容可以帮助 :)
2个回答

14

@XmlElementWrapper可以完成此任务:

@XmlElementWrapper(name="Wrapper")
@XmlElement(name="Channel")
private List<Channel> channels;

对于更高级的情况,您可以使用EclipseLink JAXB(MOXy)中的@XmlPath扩展:


这是我目前所拥有的。我仍在尝试消除助手对象的需要。此示例需要EclipseLink JAXB (MOXy)

模型对象

您的模型对象是:

package example;

import java.util.ArrayList;
import java.util.List;

public class Wrapper {

    private List<Channel> channels = new ArrayList<Channel>();

    public List<Channel> getChannels() {
        return channels;
    }

    public void setChannels(List<Channel> channels) {
        this.channels = channels;
    }

}

并且:

package example;

import javax.xml.bind.annotation.XmlID;

public class Channel {

    private String id;
    private String type;
    private String name;

    @XmlID
    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

}

辅助对象

我的当前解决方案涉及一些辅助对象:

package example.adapted;

import java.util.List;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import example.Channel;
import example.Wrapper;

@XmlRootElement(name="Output")
@XmlType(propOrder={"channels", "channelNames"})
public class AdaptedWrapper {

    private Wrapper wrapper = new Wrapper();
    private List<ChannelName> channelNames;

    @XmlTransient
    public Wrapper getWrapper() {
        for(ChannelName channelName : channelNames) {
            channelName.getChannel().setName(channelName.getName());
        }
        return wrapper;
    }

    @XmlElementWrapper(name="Wrapper")
    @XmlElement(name="Channel")
    public List<Channel> getChannels() {
        return wrapper.getChannels();
    }

    public void setChannels(List<Channel> channels) {
        wrapper.setChannels(channels);
    }

    @XmlElementWrapper(name="Wrapper")
    @XmlElement(name="ChannelName")
    public List<ChannelName> getChannelNames() {
        return channelNames;
    }

    public void setChannelNames(List<ChannelName> channelNames) {
        this.channelNames = channelNames;
    }

}

并且:

package example.adapted;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlIDREF;

import example.Channel;

public class ChannelName {

    private String name;
    private Channel channel;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @XmlIDREF
    @XmlElement(name="id")
    public Channel getChannel() {
        return channel;
    }

    public void setChannel(Channel channel) {
        this.channel = channel;
    }

}

示例代码

package example;

import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;

import example.adapted.AdaptedWrapper;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(AdaptedWrapper.class);

        File xml = new File("input.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        AdaptedWrapper adaptedWrapper = (AdaptedWrapper) unmarshaller.unmarshal(xml);
        Wrapper wrapper = adaptedWrapper.getWrapper();

        for(Channel channel : wrapper.getChannels()) {
            System.out.println(channel.getName());
        }
    }

}

好的,我已经解决了零参数构造函数错误。这是由于我对jaxb.index文件中应该放什么的误解,我也将我的XMLHelper类列出来了!如果有人想知道我所遵循的示例,请参阅MOXy Spring JAXB注释指南 - andyb
非常感谢@Blaise的帮助。我现在离开了我的代码,但明天会尝试一下。你建议的谓词解决方案可能已经接近完成了。我计划先通过简单的XSLT运行XML,以添加必要的属性来链接元素。 - andyb
感谢提供XSLT链接和答案更新。我怀疑可能需要一些辅助对象。我已决定在绑定之前使用XSLT将XML弯曲成更好的结构,主要是因为我很了解XSLT / XPath,而且它也不涉及额外的对象,更简单。我认为MOXy谓词映射功能不适用,因为我需要基于谓词上的可变属性值匹配来组合XML元素。即对于Channel@id=1,我需要找到ChannelName[@id="1"]/Name,然后再找到另一个元素以匹配@id=2。 - andyb
话虽如此,我仍然会使用MOXy,因为我喜欢@XmlPath注释。我也会关注它的发展。我只是认为,尝试同时绑定和重新组织一个非常扁平的XML结构太复杂了,除非使用辅助对象或转换。我以为我可以通过一些非常聪明的注释来解决问题。无论如何,再次感谢,答案已被接受 :-) - andyb
1
@andyb - 现在可用谓词功能,更多信息请参见:http://bdoughan.blogspot.com/2011/03/map-to-element-based-on-attribute-value.html - bdoughan
显示剩余4条评论

2
您可以通过在JAXB中自动化此过程来节省编码时间:
使用以下链接创建XML模式,并将其保存为output.xsd文件: http://www.xmlforasp.net/CodeBank/System_Xml_Schema/BuildSchema/BuildXMLSchema.aspx 从项目根目录(.)运行批处理脚本文件(将其命名为output.bat),只使用JDK,因为只有JDK具有xjc.exe工具(填写必要的详细信息)。
"C:\Program Files\Java\jdk1.6.0_24\bin\xjc.exe" -p %1 %2 -d %3

where...

syntax: output.bat %1 %2 %3
%1 = target package name
%2 = full file path name of the generated XML schema .xsd 
%3 = root source folder to store generated JAXB java files

示例:

假设项目文件夹的组织方式如下:

.
\_src

请在命令提示符下从(.)运行以下命令:

output.bat com.project.xml .\output.xsd .\src

它会创建几个文件:
.
\_src
  \_com
    \_project
      \_xml
        |_ObjectFactory.java
        |_Output.java

接下来,您可以创建一些有用的方法来操作Output对象:

private JAXBContext jaxbContext = null;
private Unmarshaller unmarshaller = null;
private Marshaller marshaller = null;

public OutputManager(String packageName) {
    try {
        jaxbContext = JAXBContext.newInstance(packageName);
        unmarshaller = jaxbContext.createUnmarshaller();
        marshaller = jaxbContext.createMarshaller();
    } catch (JAXBException e) {
    }
}

public Output loadXML(InputStream istrm) {

    Output load = null;

    try {
        Object o = unmarshaller.unmarshal(istrm); 

        if (o != null) {

            load = (Output) o;

        }

    } catch (JAXBException e) {

        JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);

    }
    return load;
}

public void saveXML(Object o, java.io.File file) {

    Output save = null;

    try {
        save = (Output) o;

        if (save != null) {
            marshaller.marshal(save, file);
        }

    } catch (JAXBException e) {

        JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);

    }
}

public void saveXML(Object o, FileOutputStream ostrm) {

    Output save = null;

    try {

        save = (Output) o;

        if (save != null) {
            marshaller.marshal(save, ostrm);
        }

    } catch (JAXBException e) {

        JOptionPane.showMessageDialog(null, e.getLocalizedMessage(), e.getClass().getSimpleName(), JOptionPane.ERROR_MESSAGE);

    }
}

这很有用,但我不认为它会解决我的问题,因为我真的想尽可能少地编写代码,同时在绑定时操作XML结构。尽可能少地编写代码并不是因为我懒,而是我真的认为这是不必要的,还会增加复杂性并需要更广泛的测试。简单、优雅的代码让我感到高兴。然而,对于你提到的链接或工具,我给予+1的答案,因为我之前不知道它。 - andyb
1
@andyb:也许你可以先自动化,然后再编辑生成的文件。 - eee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接