【问题标题】:JAXB unmarshalling multiple XML elements into single classJAXB 将多个 XML 元素解组为单个类
【发布时间】:2011-07-16 13:42:34
【问题描述】:

我有以下 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&lt;Channel&gt;

这可以自动使用@Xml... 注释来完成吗?我目前正在使用 JAXB 解组为单独的 @XmlElement(name="Channel")@XmlElement(name="ChannelName") 类列表,然后在 Channel 上对瞬态 ChannelName/name 进行后处理,但我认为必须有一种更简单的自动化方法来映射这些元素。还是 XSLT 的工作?

知道 XML 是作为 HTTP 文件 POST 文件传入的,并且我使用的是 Spring 3、Java 和 Hibernate,这可能会有所帮助。我希望EclipseLink JAXB (MOXy) 中的某些内容可能会有所帮助:)

【问题讨论】:

    标签: java xml jaxb eclipselink moxy


    【解决方案1】:

    @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());
            }
        }
    
    }
    

    【讨论】:

    • 希望您能加入@Blaise :) 我查看了 XmlPath,但不知道如何查找给定 ID 的名称。因此,当解组第一个 Channel 时,我想取的 XPath 名称是 ChannelName[id="1"]/name,但看不到如何插入 id。我现在就看看你的博客。跨度>
    • @andyb - 我们目前正在开发相关功能(请参阅bugs.eclipse.org/339596)。初始阶段针对属性的条件(即 ChannelName[@id="1"]/Name)。您需要编组还是取消编组?
    • @Blaise - 我只需要解组,看起来谓词功能可能是解决方案,因为我需要从文档其他地方的元素中获取数据?另外,我看过你的(优秀的)博客,几乎让 MOXy 在春天工作。我花了一段时间才了解 jaxb.properties 和 jaxb.in​​dex 文件。我目前收到class org.springframework.oxm.jaxb.Jaxb2Marshaller$ByteArrayDataSource requires a zero argument constructor 异常。我正在使用 Eclipse Persistence Services 2.3.0.v20110312-r9123
    • 好的,所以我已经解决了零参数构造函数错误。这是因为我误解了 jaxb.index 文件中应该包含的内容,并且我也列出了我的 XMLHelper 类!如果有人想知道我所遵循的示例,请参阅MOXy Spring JAXB Annotations guide
    • @andyb - 谓词功能现已推出,更多信息请参阅:bdoughan.blogspot.com/2011/03/…
    【解决方案2】:

    您可以通过在 JAXB 中自动执行此过程来节省编码时间:

    使用下面的链接为您的 XML 创建一个 XML 架构,并将其保存为 output.xsd 文件: http://www.xmlforasp.net/CodeBank/System_Xml_Schema/BuildSchema/BuildXMLSchema.aspx

    使用 JDK 从项目根文件夹 (.) 运行下面的批处理脚本文件(将其命名为 output.bat),因为只有 JDK 具有 xjc.exe 工具(填写必要的详细信息):

    "C:\Program Files\Java\jdk1.6.0_24\bin\xjc.exe" -p %1 %2 -d %3
    

    在哪里...

    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:也许你可以先自动化,然后再编辑生成的文件。
    猜你喜欢
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 2011-10-08
    • 1970-01-01
    • 1970-01-01
    • 2012-06-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多