是否可以以编程方式配置 JAXB? [英] Is it possible to programmatically configure JAXB?

查看:25
本文介绍了是否可以以编程方式配置 JAXB?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两个 JavaBeans PersonAddress.

Say I have two JavaBeans Person and Address.

如果我创建一个 Person 对象列表,我想编组如下:

If I create a list of Person objects, I'd like to marshal to something like this:

<persons>
  <person>...</person>
</persons>

可以使用此处描述的技术:使用 JAXB 解组/编组列表

It's possible to use the technique described here: Using JAXB to unmarshal/marshal a List<String>

通过使用 @XmlRootElement(name = "persons")@XmlElement(name = "person") 注释 JaxbList,然后可以编组到上面的 XML.

By annotating JaxbList with @XmlRootElement(name = "persons") and @XmlElement(name = "person"), then it's possible to marshal to the XML above.

但是,如果能够重用相同的 JaxbList<T> 类来编组 Address 对象列表,那就太好了.实际上,我还会有许多其他类型的豆子.我可以使用类似的东西:

But, it'd be nice to be able to reuse the same JaxbList<T> class to also marshal a list of Address objects. And in reality, I will have many other types of beans. I can go with something like:

<list>
   <item xsi:type="person" xmlns:xsi="http://www.w2.org/2001/XmlSchema-instance"></item>
</list>

但是,理想情况下,最好将list"替换为类名的复数形式,将item"替换为类名.

But, ideally, it'd be nice to have it replace "list" with the plural version of class name and "item" with the class name.

那么,是否可以在运行时以编程方式配置 JaxbContext 或其他东西,并在 @XmlRootElement@XmlElement 内设置name 的值?代码>?

So, is it possible to programmatically configure the JaxbContext or something during runtime and essentially set the value of the name inside @XmlRootElement and @XmlElement?

或者有什么其他方法可以让这个工作而不必为每个 bean 类型编写 JaxbList 的单独实现?也许 XmlJavaTypeAdapter 可以实现这种东西?

Or any other way to get this working without having to write a separate implementation of JaxbList for every bean type? Maybe XmlJavaTypeAdapter can achieve this sort of thing?

更新下面接受的@Blaise Doughan 的解决方案效果很好.对于我的用例,我需要直接从 Java 对象转到 XML,这是有效的(注意这不是我的完整实现,它只是用于演示的伪代码):

Update @Blaise Doughan's solution accepted below works great. For my use case, I needed to go straight from Java object to XML, here's what worked (note this is not my full implementation, it's sort of just pseudo code for demonstration):

    //JAXBContext is thread safe and so create it in constructor or 
    //setter or wherever:
    ... 
    JAXBContext jc = JAXBContext.newInstance(Wrapper.class, clazz);
    ... 

    public String marshal(List<T> things, Class clazz) {

      //configure JAXB and marshaller     
      Marshaller m = jc.createMarshaller();
      m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

      //Create wrapper based on generic list of objects
      Wrapper<T> wrapper = new Wrapper<T>(things);
      JAXBElement<Wrapper> wrapperJAXBElement = new JAXBElement<Wrapper>(new QName(clazz.getSimpleName().toLowerCase()+"s"), Wrapper.class, wrapper);

      StringWriter result = new StringWriter();
      //marshal!
      m.marshal(wrapperJAXBElement, result);

      return result.toString();

    }

推荐答案

您可以创建一个通用的 Wrapper 对象,如下所示:

You could create a generic Wrapper object like the following:

包装

您可以使用@XmlAnyElement(lax=true) 注释的List 属性创建一个通用包装类.用于填充此列表的对象类型将基于其根元素(请参阅:http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html).

You could create a generic wrapper class with a List property annotated with @XmlAnyElement(lax=true). The type of the object used to populate this list will be based on its root element (see: http://blog.bdoughan.com/2010/08/using-xmlanyelement-to-build-generic.html).

package forum13272288;

import java.util.*;
import javax.xml.bind.annotation.XmlAnyElement;

public class Wrapper<T> {

    private List<T> items = new ArrayList<T>();

    @XmlAnyElement(lax=true)
    public List<T> getItems() {
        return items;
    }

}

地址

您需要使用 @XmlRootElement 注释列表中可能的内容.

You will need to annotate the possible contents of the list with @XmlRootElement.

package forum13272288;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Address {

}

package forum13272288;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Person {

}

演示

下面的演示代码演示了如何使用 Wrapper 类.由于根元素可能不同,您需要指定要解组到包装器类.或者,您可以利用 @XmlElementDecl 批注将多个根元素与包装类相关联(请参阅:http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).

The demo code below demonstrates how to use the Wrapper class. Since the root element can be different you will need to specify that you want to unmarshal to the wrapper class. Alternatively you could leverage the @XmlElementDecl annotation to associate multiple root elements with the wrapper class (see: http://blog.bdoughan.com/2012/07/jaxb-and-root-elements.html).

package forum13272288;

import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Wrapper.class, Person.class, Address.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        StreamSource personsXML = new StreamSource("src/forum13272288/persons.xml");
        JAXBElement<Wrapper> wrapper1 = unmarshaller.unmarshal(personsXML, Wrapper.class);
        marshaller.marshal(wrapper1, System.out);

        StreamSource addressesXML = new StreamSource("src/forum13272288/addresses.xml");
        JAXBElement<Wrapper> wrapper2 = unmarshaller.unmarshal(addressesXML, Wrapper.class);
        marshaller.marshal(wrapper2, System.out);
    }

}

输出

以下是运行演示代码的输出.文件 persons.xmladdresses.xml 看起来就像有相应的输出.

Below is the output from running the demo code. The files persons.xml and addresses.xml look just like there corresponding output.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<persons>
    <person/>
    <person/>
</persons>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addresses>
    <address/>
    <address/>
</addresses>

了解更多信息

这篇关于是否可以以编程方式配置 JAXB?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆