介绍一下XMLBeans,它真的很好用

XMLBeans 提供一种直观的方法处理XML,让你更容易使用Java访问和管理XML数据和文档。

XMLBeans 中 有关XML 的特性

  • 为XML数据提供一个友好的Java面向对象的视图,不会在访问原始本地的XML结构中迷失。
  • 使 用XMLBeans的过程中,XML作为一个文档的完整性并没有丢失。面向XML的APIs通常分类XML,目的是为了绑定它的多个部分。使用 XMLBeans,整个XML实例文档以一个整体的方式处理。XML数据以XML的方式存储再内存中。这表示文档的次序将被保留,就如原始带有空白的元素 内容。
  • 从schema那里生成类型,以类似JavaBean的 get 和 set 方式访问XML实例。
  • 它一开始就 以 XML schema 为中心而设计 — XMLBeans支持所有的XML schema定义。
  • 快速访问XML

XMLBeans 的起始点是 XML schema。一个schema(包含在一个XSD文件中)是一个XML文档,它定义了其他的XML文档必须遵守的规则集合。XML schema 规范提供一个丰富的数据模型,允许你在数据中表达复杂的结构和约束。例如,一个XML schema 能强行控制一个文档中的数据排序,或者一些特殊值的约束(例如,生日的数据必须在1900年之后)。遗憾的是,如果在java中没有自己编写代码的话,想 这样的强制规则的能力是典型无效的。XMLBeans 尊重 schema 的 约束。

注意:一个XML schema 定义了一个XML文档的规则,一个XML实例是一个符合这个schema的文档。 你编译一个schema(XSD)文件,生成一个schema镜像的Java接口集。 你就可以使用这些类型来处理符合这个schema的XML实例文档。你绑定一个XML实例文档到这些类型上;通过这些java接口来改变下面的XML内 容。

前面处理XML的选项,包含使用XML编程接口(如 DOM 或者 SAX)或者一个XML 编组/绑定工具(如JAXB)。因为它缺少鉴定的面向schema的类型,一个面向DOM的模型导航是更加乏味的,所以需要一个更加易懂的完全对象模型。 JAXB提供对XML schema规法的支持,但它仅仅处理一个子集;XMLBeans则支持所有的特性。同样,一个XML的方式在内存中存储数据, XMLBeans 能够减少 编组和反编组 的消耗。

使用它的schema来访问XML

瞄一下,你可以使用XMLBeans来做哪些事情, 来看看这个使用XML做购物订单的例子。这个购物订单的XML包含双方的交换数据,比如两个公司。 双方都需要能够以来一个统一的消息模型,和一个 schema 来指定共同的基础。
一个购物订单XML实例表现如下:

<po:purchase-order xmlns:po=”http://openuri.org/easypo”>
<po:customer>
<po:name>张三</po:name>
<po:address>北京市海淀区</po:address>
</po:customer>
<po:date>2003-01-07T14:16:00-05:00</po:date>
<po:line-item>
<po:description>Linux 高级配置详解</po:description>
<po:per-unit-ounces>5</po:per-unit-ounces>
<po:price>21.79</po:price>
<po:quantity>2</po:quantity>
</po:line-item>
<po:line-item>
<po:description>Linux 核心</po:description>
<po:per-unit-ounces>5</po:per-unit-ounces>
<po:price>19.89</po:price>
<po:quantity>2</po:quantity>
</po:line-item>
<po:shipper>
<po:name>EMS</po:name>
<po:per-ounce-rate>0.74</po:per-ounce-rate>
</po:shipper>
</po:purchase-order>

这 个XML包含一个根元素,purchase-order,它拥有四种子元素:customer,date,line-item,和shipper。从一种 直观的,面向对象的视角来看这个XML,会得到一个对象,表现出 purchase-order 元素,并且它提供方法来获取 date 以及下面的对象,包括customer,line-item,和 shipper 元素。后面的三个元素也有自己的方法来获取内部的日期。

先来看看 schema

下面的XML是前面购物订单XML的schema。它定义了XML的模型–其中的元素是什么,显示的顺序,哪个是哪个的子元素等等。

<xs:schema targetNamespace=”http://openuri.org/easypo”
xmlns:po=”http://openuri.org/easypo”
xmlns:xs=”http://www.w3.org/2001/XMLSchema”
elementFormDefault=”qualified”>

<xs:element name=”purchase-order”>
<xs:complexType>
<xs:sequence>
<xs:element name=”customer” type=”po:customer”/>
<xs:element name=”date” type=”xs:dateTime”/>
<xs:element name=”line-item” type=”po:line-item” minOccurs=”0″ maxOccurs=”unbounded”/>
<xs:element name=”shipper” type=”po:shipper” minOccurs=”0″/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name=”customer”>
<xs:sequence>
<xs:element name=”name” type=”xs:string”/>
<xs:element name=”address” type=”xs:string”/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=”line-item”>
<xs:sequence>
<xs:element name=”description” type=”xs:string”/>
<xs:element name=”per-unit-ounces” type=”xs:decimal”/>
<xs:element name=”price” type=”xs:double”/>
<xs:element name=”quantity” type=”xs:int”/>
</xs:sequence>
</xs:complexType>
<xs:complexType name=”shipper”>
<xs:sequence>
<xs:element name=”name” type=”xs:string”/>
<xs:element name=”per-ounce-rate” type=”xs:decimal”/>
</xs:sequence>
</xs:complexType>
</xs:schema>

这个 schema 为描述了购物订单的XML实例,定义如下:

  • 定 义三个 complex 类型– customer, line-item, 和 shipper 。 这三种类型用于作为 purchase-order 元素的子元素。 在 schema 中 ,一个complex类型定义了一个拥有子元素和属性的元素。comlex 类型内部的 sequence 元素将列出它的子元素。这些是global类型。因为它们都位于schema的顶层。(换句话讲,仅仅位于shcema根元素之下)。这表示它们可以在 schema的任何位置引用。
  • 在complex类型中使用简单类型。name,address,和description元素是典型的简单类型。也叫内建类型。一个内建类型(以“xs”为前缀)是schema规范的一部分。(规范定义了46中内建类型。)
  • 一个叫purchase-order的全局元素。这个元素的定义中,内部包含一个complex类型定义,指定purchase-order的子元素。注意complex类型含有本schema的其他complex类型的引用。

换句话讲,schema定义子元素的类型,并且指明它们在根元素purchase-order中的位置。当你使用XMLBeans编译象这样的XSD文件时,将生成一个JAR文件,内含来自该schema的接口。

编写使用接口的Java代码

再 你的应用中使用XMLBean接口,可以在代码中使用新的类型来处理基于这个schema的XML。下面就是一个例子,提出购物订单XML中的每一条 item信息,计算items数量,并计算它们的总价。特别要看看 printItems 方法,它接受包含一个购物订单的XML文件对象。

package docs.xmlbeans;

import java.io.File;
import org.apache.xmlbeans.*;
import org.openuri.easypo.PurchaseOrderDocument;
import org.openuri.easypo.PurchaseOrder;
import org.openuri.easypo.LineItem;

public class POHandler
{
public static void printItems(File po) throws Exception
{
/*
* All XMLBeans schema types provide a nested Factory class you can
* use to bind XML to the type, or to create new instances of the type.
* Note that a “Document” type such as this one is an XMLBeans
* construct for representing a global element. It provides a way
* for you to get and set the contents of the entire element.
*
* Also, note that the parse method will only succeed if the
* XML you’re parsing appears to conform to the schema.
*/
PurchaseOrderDocument poDoc =
PurchaseOrderDocument.Factory.parse(po);

/*
* The PurchaseOrder type represents the purchase-order element’s
* complex type.
*/
PurchaseOrder po = poDoc.getPurchaseOrder();

/*
* When an element may occur more than once as a child element,
* the schema compiler will generate methods that refer to an
* array of that element. The line-item element is defined with
* a maxOccurs attribute value of “unbounded”, meaning that
* it may occur as many times in an instance document as needed.
* So there are methods such as getLineItemArray and setLineItemArray.
*/
LineItem[] lineitems = po.getLineItemArray();
System.out.println(“Purchase order has ” + lineitems.length + ” line items.”);

double totalAmount = 0.0;
int numberOfItems = 0;

/*
* Loop through the line-item elements, using generated accessors to
* get values for child elements such a description, quantity, and
* price.
*/
for (int j = 0; j < lineitems.length; j++)
{
System.out.println(” Line item: ” + j);
System.out.println(
” Description: ” + lineitems[j].getDescription());
System.out.println(” Quantity: ” + lineitems[j].getQuantity());
System.out.println(” Price: ” + lineitems[j].getPrice());
numberOfItems += lineitems[j].getQuantity();
totalAmount += lineitems[j].getPrice() * lineitems[j].getQuantity();
}
System.out.println(“Total items: ” + numberOfItems);
System.out.println(“Total amount: ” + totalAmount);
}
}

注意,从schema那里生成的类型,反映出XML里面有什么:

  • 一个 PurchaseOrderDocument 表示全局的根元素
  • 一个 getPurchaseOrder 方法返回一个 PurchaseOrderDocument.PurchaseOrder 类型,内含子元素,含有 line-item。一个 getLineItemArray 方法返回一个  LineItem 数组。
  • 其他的方法,如个体Quanity,个体Price,等等,按照schema的描述,返回相应的line-item元素的子元素相应的内容。
  • 包含这些类型的包的名称,派生自 schema 的 目标 namespace。

生成的类型名遵守Java的编码规范。同样,分析XML文件的例子,其他分析方法,支持 一个 Java InputStream 对象,一个Reader对象等等。

前面的Java代码执行后在控制台中输出如下:

Purchase order has 3 line items.
Line item 0
Description: Burnham’s Celestial Handbook, Vol 1
Quantity: 2
Price: 21.79
Line item 1
Description: Burnham’s Celestial Handbook, Vol 2
Quantity: 2
Price: 19.89
Total items: 4
Total amount: 41.68

从schema那里创建新的XML实例

前面你看到XMLBeans提供一个factory类,可以使用它来创建新的实例。下面的例子,创建一个新的purchase-order元素,然后增加一个customer子元素。然后插入name和address子元素,创建元素并设置其值,仅需调用一个set方法。

public PurchaseOrderDocument createPO()
{
PurchaseOrderDocument newPODoc = PurchaseOrderDocument.Factory.newInstance();
PurchaseOrder newPO = newPODoc.addNewPurchaseOrder();
Customer newCustomer = newPO.addNewCustomer();
newCustomer.setName(“Doris Kravitz”);
newCustomer.setAddress(“Bellflower, CA”);
return newPODoc;
}

下面就是这个XML的结果。注意XMLBeans在schema的基础上指派正确的名字空间,用 一个 “ns1″ (或者,”namespace 1″)前缀。实际上,前缀并不重要–它的 namespace URI 定义了名字空间,前缀仅仅是表示的标记。

<ns1:purchase-order xmlns:ns1=”http://openuri.org/easypo”>
<ns1:customer>
<ns1:name>Doris Kravitz</ns1:name>
<ns1:address>Bellflower, CA</ns1:address>
</ns1:customer>
</ns1:purchase-order>

注意,所有继承自XmlObject的所有类型(包含那些由schema生成的),都提供一个Factory类。要浏览整个类型系统找到哪种XmlObject合适,查看 XMLBeans Support for Built-In Schema Types 。查询参考信息,看 XmlObject Interface

XMLBeans 的层次

前 面例子中的生成的类型是XMLBean类型层次的一部分。层次是一种方法,XMLBeans通过它来直观的展示schema。 在层次的顶部是XmlObject,XMLBeans类型的基本接口。在这之下,有两种主要的类型分类:生成类型,表现用户派生的schema类型;其次 是内建的schema类型。这里已经介绍过生成类型。要得到更多的信息,查看 Java Types Generated from User-Derived Schema Types。

内建的类型支持

除 了从给出的schema中生成类型外,XMLBeans提供46中Java类型,对应着XML schema 规范中内建的46种类型。 如 schema 定义的 xs:string, xs:decimal ,和 xs:int ,对应着 XMLBeans 则是 XmlString, XmlDecimal,和XmlInt。这些对象都是从XmlObject(对应内建schema类型的 xs:anyType)中派生。

XMLBeans 提供一种方法,让你能想内建的类型般处理XML数据。另外,如你在前面的例子中看到的,对于大部分的类型,method将返回原Java类型,如int。下面两行代码都是返回元素值的数量,但返回的类型却是不同的。

// Methods that return simple types begin with an “x”.
XmlInt xmlQuantity = lineitems[j].xgetQuantity();
// Methods that return a natural Java type are unadorned.
int javaQuantity = lineitems[j].getQuantity();

在感觉上,两个方法都是返回元素数量。getQuantity方法更深入一步,转换元素的值到原生Java类型。(XMLBeans提供方法来检查你要处理的XML)

如果你知道一点关于XML schema的,XMLBeans 的类型将看起来更直观。如果不,你将需要更多的练习使用你自己的schemas和XML实例来试验XMLBeans。

需要更多的信息关于schema的类型生成,看 Methods for Types Generated From Schema 。需要更多的信息关于XMLBeans如何展示内建的schema类型,看 XMLBeans Support for Built-In Schema Types

使用XQuery表达式

用XMLBeans,你可以使用XQuery来查询到XML指定的数据块。XQuery有时被认为是“SQL for XML”,因为提供一套机制直接访问XML文档中的数据, 就如SQL提供一套机制来访问数据库中的数据。

XQuery 从 XPath 那里借用了一些语法,在XML指定内部数据的语法。下面的例子,返回所有价格子元素的值小于或等于20.00的line-item元素。

PurchaseOrderDocument doc = PurchaseOrderDocument.Factory.parse(po);

/*
* The XQuery expression is the following two strings combined. They’re
* declared separately here for convenience. The first string declares
* the namespace prefix that’s used in the query expression; the second
* declares the expression itself.
*/
String nsText = “declare namespace po = ‘http://openuri.org/easypo’; “;
String pathText = “$this/po:purchase-order/po:line-item[po:price <= 20.00]“;
String queryText = nsText + pathText;

XmlCursor itemCursor = doc.newCursor().execQuery(queryText);
System.out.println(itemCursor.xmlText());

这 些代码在文档的开始位置创建一个指针。 使用 XmlCursor 接口的 execQuery 方法来执行查询表达式。 在这个例子中, 方法的参数是一个Xquery表达式,说出”从指针的位置开始,历遍整个purchase-order 元素,或者那些价格值小于或等于20.00的line-item。这里$this变量表示“当前位置”。需要更多关于XQuery的信息,到W3C的网站 上,查看 XQuery 1.0: An XML Query Language

使用XML指针

在 前面的例子中,想必你已经注意到 XmlCursor接口。处理提供一种执行XQuery表达式的方法以外,一个XML 指针提供一个优良的去处杂乱数据的模型来处理数据。XML 指针 API,类似 DOM的 对象API, 是一种简易的方法指向数据的某些块。因此,就如一个指针,帮助历遍一个词处理文档,XML指针定义一个再XML的位置,在那里对选择的XML执行动作。

在 没有schema的情况下,指针是个历遍XML文档的理想工具。一旦你获得意想的位置的指针,你可以对它进行多种操作。例如,你可以set或者get 值,insert或者removeXML片段,copy片段从XML文档的一个部分到其他的部分,或者对XML文档做出其他的fine-grained改 变。

下面的例子,使用XML指针来转移到 customer 元素的 name 子元素。

PurchaseOrderDocument doc =
PurchaseOrderDocument.Factory.parse(po);

XmlCursor cursor = doc.newCursor();
cursor.toFirstContentToken();
cursor.toFirstChildElement();
cursor.toFirstChildElement();
System.out.println(cursor.getText());

cursor.dispose();

发生了哪些事情呢?如早期的例子,代码装入一个XML文件。装入后,代码在开始的位置创建一个指针。多次移动指针,直至它到达name元素的位置。使用getText方法获取了元素的值。
这仅仅是对XML指针的简介。要获得关于指针的更多信息,参考 Navigating XML with Cursors.

接下来要干嘛
  • XMLBeans 提供处理XML的直接方法,特别是在你了解schema的情况下。 如何你正在访问基于schema的XML,你会发现使用schema生成的类型来访问XML是最有效的。要做到这点,需要你先编译schema来生成接 口。要得到更多关于使用schema生成的XMLBeans类型的资料,参考 Java Types Generated From User-Derived Schema Types and Methods for Types Generated From Schema
  • 你 可能有兴趣阅读更多关于XMLBeans基于的类型系统,特别是在你使用schema生成的类型的情况下。 XMLBeans提供层次型的类型系统,对应到XML schema规范。如果你是用schema,你会发现它将帮助你了解这些类型如何工作。了解更多的信息,参考 XMLBeans Support for Built-In Schema Types and Introduction to Schema Type Signatures
  • XMLBeans 提供通过XQuery来访问XML,它从Xpath那里借来路径语法。使用XQuery,不管在有没有schema的情况下,你都可以指定特定的XML片段。了解更多关于XMLBeans中XQuery和XPath的内容,参考 Selecting XML with XQuery and XPath
  • 你可使用XmlCursor接口进行 fine-grained 航行和操作XML。获取更多信息,参考 Navigating XML with Cursors

注意:xbean.jar 文件 包含 XMLBeans 库,在功能上完全能作为一个独立库。

相关主题

XMLBeans Samples

This entry was posted in 开发工具. Bookmark the permalink.

Leave a Reply