当前位置: 首页 > 工具软件 > JAXB > 使用案例 >

JAXB-3 JAXB API

劳灵均
2023-12-01

3 JAXB API

该章节将讲述,如何通过 JAXB 提供的接口(API),将 Java 类对象转换为 XML 文档。

JAXB API
Java Class Object
XML Doc

3.1 JAXBContext 解读

被缓存的JAXBContext,为了性能上的考虑,将会对JAXBContext做缓存,不过缓存使用到了WeakReference,不用担心 GC 问题。
每次在创建JAXBContext实例时,JAXBContext内部都需要维护好Java类和XML之间的映射关系,这个操作十分消耗性能。不过JAXBContext是线程安全的,可以共享。一种较好的做法是,在程序初始化时,传入所有的Class,在使用时直接调用创建好的JAXBContext实例,而不是在每次使用时创建。
JAXBContext 是线程安全的,但是 Marshaller, Unmarshaller 都不是线程安全的。
在多线程环境下,应该使用类似下面的方式来初识化JAXBContext。

class MyServlet extends HttpServlet {
    static final JAXBContext context = initContext();
    private static JAXBContext initContext() {
        return JAXBContext.newInstance("....", MyServlet.class.getClassLoader());
    }
}

同时,在使用JAXB时,可以类似下面的方式获取Unmarshaller/Marshaller。

public void doGet(HttpServletRequest req, HttpServletResponse resp) {
    Unmarshaller u = context.createUnmarshaller();
    u.unmarshal(...);
}

JAXBContext 提供了四种静态方法,方便使用者直接调用。为什么是四种而不是四个呢?因为它们有很多重载的方法,名称都是相同的,参数不同而已。

3.1.1 newInstance()

应用程序可以通过多种途径获得 JAXBContext 实例,它们拥有相同的函数名,只是参数不同。
使用类名 Xxx.class
创建 JAXB 实例,最常见的方式是传入特定的class.

// 使用类名 Xxx.class
JAXBContext instance = JAXBContext.newInstance(Student.class);

使用包路径加上类名 com.x.xx.Xxx.class
如果存在同名的Java对象,则可以指定完整路径:

// 使用包路径加上类名 com.x.xx.Xxx.class
JAXBContext instance = JAXBContext.newInstance(com.example.bean.Student.class);

多个类之间使用逗号分隔开
如果有多个对象需要注册,则可以:

// 多个类之间使用逗号分隔开
JAXBContext instance = JAXBContext.newInstance(Student.class, Teacher.class);

传入完整包名
或者也可以对某一个package包下所有的对象编组:

// 传入完整包名
JAXBContext instance = JAXBContext.newInstance("com.example.bean");

这时候并不是指定的包中所有的Class都会用来创建JAXBContext。按照JAXB的规范,我们需要在对应的包中创建一个 jaxb.index 文件,然后在其中指定创建JAXBContext时需要用到的Class,每个Class名称占一行。否则,会报错 “com.example.bean” 不包含 ObjectFactory.class 或 jaxb.index
完整包名之间使用冒号分隔开
甚至对多个package包下所有的对象编组:

// 完整包名之间使用冒号分隔开
JAXBContext instance = JAXBContext.newInstance("com.example.bean:com.example.pojo");

3.1.2 createMarshaller()

创建一个Marshaller对象,用于将Java内容转换为XML数据。
对使用工厂方法手动创建的内容进行编组与unmarshal操作结果的内容进行编组没有本质区别。客户端可以将Java内容编组成java.io.OutputStream或者java.io.Writer的XML数据。编组程序可以生成已经注册号的SAX2事件流,也可以生成一个DOM节点对象。

3.1.3 createUnmarshaller()

创建一个可以用来将 XML 数据转换为 java 内容树的 Unmarshaller 对象。 该方法允许将模式中声明的任何全局 XML 元素解组为实例文档的根元素。

3.1.4 createBinder()

创建一个可用于关联/原地解组/编组操作的 Binder 对象。如果不传参数,默认使用W3C DOM创建一个Binder。

3.2 静态方法提供者 JAXB

javax.xml.bind.JAXB是2.1版本新增的工具类,其中所有的 public 方法均为静态方法,可以直接通过类名调用。它存在的意义就是帮助使用者简单,快速上手JAXB。

3.2.1 JAXB.unmarshal(…)

有很多重载的方法,方便使用者传入不同类型的参数。使用者不需要关心实现的细节,传入你需要转换的数据,加上需要生成的类,就能得到需要的数据。

public static <T> T unmarshal( URI xml, Class<T> type ) {}

返回值使用的是泛型T,可以得到任意对象

3.2.2 JAXB.marshal(…)

同样有很多重载的方法,方便使用者传入不同类型的参数。

public static void marshal( Object jaxbObject, URL xml ) {}

第一个参数是对象Obj,可以传入任意对象

注:自 JAXB 1.0 以后,验证已经发生了很大的变化。Validator 类已经废弃,成为了一个可选项。这意味着建议您不要使用此类,实际上,根据您的 JAXB 提供者,该类甚至根本不可用。

3.3 JAXBContext Marshaller 的转换参数

1、 格式化输出 Marshaller.JAXB_FORMATTED_OUTPUT
默认的序列化操作,结果是正确的,但是显示成一行不便于阅读,可以通过添加属性JAXB_FORMATTED_OUTPUT 来修正:

2、 编码字符集 Marshaller.JAXB_ENCODING
如果需要改输出XML的字符编码,可以添加属性JAXB_ENCODING:

@Test
public void test2() throws JAXBException {
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.setProperty(Marshaller.JAXB_ENCODING, "ISO-8859-1");
    marshaller.marshal(one, System.out);
}

得到的结果:

<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
<one>
    <name>Test one</name>
</one>

已经不再是默认的UTF-8。

3、 描述文件地址 Marshaller.JAXB_SCHEMA_LOCATION
有时候,程序可能需要指定 xsi:schemaLocation,则可以添加属性JAXB_SCHEMA_LOCATION:

@Test
public void test3() throws JAXBException {
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.setProperty(Marshaller.JAXB_SCHEMA_LOCATION, "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd");
    marshaller.marshal(one, System.out);
}

得到的结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<one xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans/spring-beans.xsd">
    <name>Test one</name>
</one>

xsi:schemaLocation 其实是 Namespace 为 “http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd” 里的 schemaLocation 属性,它定义了 XML Namespace 和对应的XSD(Xml Schema Definition)文档的位置的关系。在Spring的配置文件中,时常能见到这个属性。

4、声明无文件地址 Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION
如果没有Namespeace,但是需要使用Schema,就需要用到JAXB_NO_NAMESPACE_SCHEMA_LOCATION:

@Test
public void test4() throws JAXBException {
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "ehcache.xsd");
    marshaller.marshal(one, System.out);
}

得到的结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<one xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd">
    <name>Test one</name>
</one>

5、 取消XML头信息 Marshaller.JAXB_FRAGMENT
JAXB_FRAGMENT 是一个多面手,在不同的输出场景下,表现出不同的效果。

@Test
public void test5_1() throws JAXBException {
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
    marshaller.marshal(one, System.out);
}

在这里指定JAXB_FRAGMENT=true,表明结果不再声明XML头信息,得到的结果:

<one>
    <name>Test one</name>
</one>

可以看到,第一行的XML声明不见了。
如果使用到了SAX方式,可以发现如下不同:

@Test
public void test5_2() throws JAXBException {
    Marshaller marshaller = context.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
//  marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);
    marshaller.marshal(one, new MyContentHandler());
}

输出结果:

startDocument
endDocument

如果将注释的一行代码放开,再次运行程序将不能得到任何输出。JAXB_FRAGMENT的默认值为false,在其他场景下也有不同的表现。

3.4 JAXB API 封装

如果要对 Student.java 进行 XML 操作(对象树与 XML 相转换),有如下步骤
1)使用 JAXBContext.newInstance(…) 初始化 JAXBContext
2)创建 Marshaller 或 Unmarshaller 实例
3)对 XML 文档进行操作

以下通过对 Student.java 的 JAXB API 封装做示例,可以通过多种方式对 Student.java 进行 XML 操作,最优的方式是对每个 XML 封装一个 Java Class,如下:

/**
 * a single ton object that is responsible for converting XML to a <Sample> object and <Sample> to an XML.
 */
public class SampleXmlSerializer {
    //  the singleton instance
    private volatile static SampleXmlSerializer instance;

    //  marshaller and unmarshaller
    private final Marshaller marshaller;      //  java to xml
    private final Unmarshaller unmarshaller;  //  xml to java

    private SampleXmlSerializer() throws JAXBException {
        //  create the JAXBContext object only here, to prevent memory leak
        JAXBContext jc = JAXBContext.newInstance(Sample.class);
        marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        unmarshaller = jc.createUnmarshaller();
    }

    /**
     * @return the singleton's instance (create it if necessary)
     * @throws JAXBException if an error occurred while initializing the object
     */
    public static SampleXmlSerializer getInstance() throws JAXBException {
        if (instance == null) {
            synchronized (SampleXmlSerializer.class) {
                //  double check the reference
                if (instance == null) {
                    instance = new SampleXmlSerializer();
                }
            }
        }
        return instance;
    }

    /**
     * serializes a request object to an XML string
     * @param request callback request
     * @return the given request serialized to an XML string
     * @throws JAXBException if an error occurs during marshaling
     */
    public String serialize(Sample request) throws JAXBException {
        //  output string
        StringWriter writer = new StringWriter();
        //  marshal the request
        synchronized (marshaller) {
            marshaller.marshal(request, writer);
        }
        return writer.toString();
    }

    /**
     * deserializes a request object from a given XML string
     * @param xmlString XML input string
     * @return callback request object that was deserialized from the input string
     * @throws JAXBException if an error occurs during unmarshalling
     * @throws ClassCastException if the deserialized object is not an instance of <Sample>
     */
    public Sample deserialize(String xmlString) throws JAXBException {
        StringReader reader = new StringReader(xmlString);
        JAXBElement<Sample> element;
        synchronized (unmarshaller) {
            element = (JAXBElement<Sample>) unmarshaller.unmarshal(reader);
        }
        return element.getValue();
    }

}

以上的 XmlSerialier 工具类,使用单实例(Singleton)保证对 Student.java 的 JAXBContext 上下文只运算一次,避免多次调用浪费运算资源;提供 serialize 和 deserialize 方法,对 Student.java 的 XML 序列化和反序列化提供支持。
另外一种方式,使用静态方法对 JAXB 的 API 进行二次封装,该方式不推荐在产品中使用。

/**
 * serializes a request object to an XML string
 * @param obj
 * @return
 */
public static String convertToXml(Object obj) {
    StringWriter sw = new StringWriter();
    try {
        JAXBContext context = JAXBContext.newInstance(obj.getClass());

        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        
        marshaller.marshal(obj, sw);
    } catch (JAXBException e) {
        e.printStackTrace();
    }
    return sw.toString();
}

采用同样的方式,可以将 marshaller 后的数据输出到其他位置。

  • 封装 marshal( Object jaxbElement, java.io.Writer writer )
    StringWriter sw = new StringWriter();
  • 封装 marshal( Object jaxbElement, java.io.Writer writer )
    FileWriter fw = new FileWriter(path);
  • 封装 marshal( Object jaxbElement, java.io.OutputStream os )
    PrintStream out = System.out;
/**
 * deserializes a request object from a given XML string
 */
public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
    Object xmlObject = null;
    try {
        JAXBContext context = JAXBContext.newInstance(clazz);
        Unmarshaller unmarshaller = context.createUnmarshaller();
        StringReader sr = new StringReader(xmlStr);
        xmlObject = unmarshaller.unmarshal(sr);
    } catch (JAXBException e) {
        e.printStackTrace();
    }
    return xmlObject;
}

支持多种输入源,

  • 封装 unmarshal( Reader reader )
    StringReader sr = new StringReader(xmlStr);
    或 FileReader fr = new FileReader(xmlPath);

上一章:JAXB-2 JAXB Annotation 注解
目录:学习 JAXB
下一章:JAXB-4 Java 类与 XML 的转换


 类似资料: