教程:关于XMLBeans的初步
介绍
本教程介绍XMLBeans的基本。通过它,你将会学到下面几点:通过schema
得到对应的xml强类型,通过xml指针的对应xml的不可知类型。本教程说
明了什么是xmlbeans和随这xmlbeans安装介绍了一些工具。
开始,你需要做以下事情:
--jdk1.4和xmlbeans version1.
--一个xmlbeans version1的安装文件。参照
http://xmlbeans.apache.org/documentation/conInstallGuide.html
--一个用于书写java代码的编辑器。
作为教程的一部分,你将创建一些文件。在xmlbeans_home目录下创建一
个新的教程目录。这里讲保存在课程中需要创建的文件。然后在教程目录
下面创建下面的子目录:class,instances,src,lib。如果你使用的是
xmlbeans version 1.0.3,层次该如下所表示。
xmlbeans-1.0.3
...
tutorials
|-------gettingstarted
|-------classes
|-------instances
|-------lib
|-------src
一些基本知识
xmlbeans version1 包括多种处理xml的相关技术。提供了3种作为补充的
技术。
A schema-oriented way to view XML instances through Java types
based on the schema.
A schema-agnostic way to traverse the full XML infoset.
A schema object model through which, in Java code, you can
examine a compiled XML schema. (Note: this aspect of XMLBeans is
not covered in this tutorial. For more information, see
Introduction to the Schema Type System.
还有更多,但是现在只是开始。上面三个对于不同的目标有不同的作用,
你可能会发现你自己在写代码的时候会在一个应用中使用这三种技术。
通过模式处理xml
xmlbeans提供通过使用模式来处理xml文档的方法。通过xmlbeans你可以
编译一个或多个模式(xsd)文件来生成java类型。通过绑定xml实例文档到
这些java类型,you provide yourself a way to access the instances
in java in a schema-oriented way.
在安装好了xmlbeans后,用你得到的xml文档进行尝试。开始,打开一个
提示符窗口,进入到xmlbeans的安装目录。如果你看装的版本是1.0.3,cd
\xmlbeans-1.0.3
如果你是按installation instruction来安装xmlbeans的,那么你该可以
执行\bin目录下的脚本。使用他们中的一个xpretty来查看一下包含在
easypo.xsd中的easypo schema:xpretty schemas\easypo\easypo.xsd
你该可以看到下面的内容:
<xs:schema
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:po="http://openuri.org/easypo"
targetNamespace="http://openuri.org/easypo"
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" maxOccurs="1"/>
</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:attribute name="age" type="xs:int"/>
<xs:attribute name="moo" type="xs:int" default="100"/>
<xs:attribute name="poo" type="xs:int" fixed="200"/>
</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:decimal"/>
<xs:element name="quantity" type="xs:integer"/>
</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一无所知,那么这里有一些基本知识:
---这个schema是一个蓝图,定义了xml文档的规则。
---定义一个更节点<purchase-order>和四个孩子节点:<customer>,
<date>, <line-item>, 和 <shipper>,且必须按顺序。
---4个子与元素中的3个有他们自己的孩子节点,分别在<complexType>下
定义。
看一下基于这个模式的xml文档。将这个xml拷贝并保存到easypo.xml,然
后讲该文件保存到tutorials\gettingstarted\instances目录。
<purchase-order xmlns="http://openuri.org/easypo">
<customer>
<name>Gladys Kravitz</name>
<address>Anytown, PA</address>
</customer>
<date>2003-01-07T14:16:00-05:00</date>
<line-item>
<description>Burnham's Celestial Handbook, Vol
1</description>
<per-unit-ounces>5</per-unit-ounces>
<price>21.79</price>
<quantity>2</quantity>
</line-item>
<line-item>
<description>Burnham's Celestial Handbook, Vol
2</description>
<per-unit-ounces>5</per-unit-ounces>
<price>19.89</price>
<quantity>2</quantity>
</line-item>
<shipper>
<name>ZipShip</name>
<per-ounce-rate>0.74</per-ounce-rate>
</shipper>
</purchase-order>
这个 xml表示一个订单:gladys,一个业余的天文爱好者,购买两本书。
因为这个xml是模式的一个实例,所以你将认识到<purchase-order>和它
的四个孩子节点<customer>, <date>, <line-item>, 和 <shipper>.
现在通过xmlbeans完成这个例子。scomp,执行模式编译的脚本,用来编
一个一个模式,或一个目录的模式。现在,适用scomp来编译easypo模式
。在windows下,适用下面的命令:
scomp -out tutorials\gettingstarted\lib\easypo.jar
schemas\sasypo\sasypo.xsd
结束后你将在lib目录下找到easypo.jar文件。
注意:如果你使用ant,你可以使用xmlbean ant task来代替scomp
编译模式后的结果
模式编译的输出不只是提供一个模式的javabean组织视图,而且
anticipate the shortcuts you're likely to want.如果你通过生成的
java类型比较一下easypo.xsd的内容,你会发现明显的相似。对于每一个
元素和模式类型,模式编译起都产生了java类型。包名相对应的是模式的
命名空间的uri。
举个例子,take another look at the <purchase-order>element
defined in the schema:
<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" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
这个片段定义了<purchase-order>元素含有一个<local>的复杂类型。这
个类型包含了有顺序的孩子元素。<date>元素被指定了xs:dateTime类型
,是一个内建的类型。其他的三个元素是定义在模式其他位置的复杂类型
。
options for accessing elements of built-in schema types
为了表现<purchase-order>元素,模式编译器生成了一个PurchaseOrder
接口,继承子java.long.Object和org.apache.xmlbeans.XmlObject.虽然
如此,你可以发现这个接口实际上包含在PurchaseOrderDocument接口。
xmlbeans把它作为全局的元素和属性(定义在模式顶层的)。这为你提供
了取得和设置这些顶层item的方法。换句话说,当你需要一个类型来提供
一些方法如getPurchaseOrder和setPurchaseOrder的时候,“document”
接口就成为了这个角色。
对于每一个<purchase-order>的4个孩子元素,purchaseOrder接口根据
javabean的规则暴露了入口。如:对于<date>元素如下:
public abstract java.util.Calendar getDate()
public abstract void setDate ( java.util.Calendar )
这是模式编译器提供到达<date>元素的一种方法,一种方便的,java-
native方法。因为<date>元素的类型xs:dateTime是模式的内建类型,模
式编译器提供了入口用另一种类型:xmlbeans定义的 来得到和设置它的
值:
public abstract org.apache.xmlbeans.XmlDateTime xgetDate()
public abstract void xsetDate( org.apache.xmlbeans.XmlDateTime )
xmlDateTime可以被看成一个罗塞塔石,使用它,你可以使用
java.util.Calendar,java.util.Date和org.apache.xmlbeans.GDate得到
和设置元素的值.关于模式的内建类型和java类型的对应关系,请参考
http://xmlbeans.apache.org/docs/2.0.0/guide/conXMLBeansSupportBu
iltInSchemaTypes.html
用户自定义的模式类型的入口
对于模式内定义的3个元素,编译器生成了分开的java类型,在入口中适
用了他们--请看
public abstract org.openuri.easypo.Customer getCustomer()
public abstract void setCustomer( org.openuri.easypo.Customer )
换句话说,你可以调用getCustomer来获得Customer的实例,然后更新这
个实例的内容。
类似的,你得到一个方便的方法:像
public abstract org.openuri.easypo.Customer addNewCustomer()
通过一个add*方法,你可以增加一个新的<customer>到<purchase-order>
元素,方法返回一个customer实例,你可以更新这个新的元素的内容。
其他为元素和属性提供的方便的方法是可选的。<shipper>元素是可选的
因为其minOccurs属性的值为0.判断这个元素是否存在和移除这个元素,
你得到下面的方法:
public boolean isSetShipper ( )
public abstract void unsetShipper ( )
对于那些出现多次的元素
模式中定义的另一个可选的元素是<line-item>.尽管如此,有一个重要的
区别-maxOccurs="unbounded",意味着它可能作为<purchase-order>的孩
子存在多个。java通常的做法是一个类型的数组-而这个正是模式编译器
所给你的。
// Get or set the whole array.
public abstract org.openuri.easypo.LineItem[] getLineItemArray (
)
public abstract void setLineItemArray (
org.openuri.easypo.LineItem[] )
// Get or set a single item.
public abstract org.openuri.easypo.LineItem getLineItemArray (
int )
public abstract void setLineItemArray( int,
org.openuri.easypo.LineItem )
// Add or remove an item.
public abstract org.openuri.easypo.LineItem insertNewLineItem(
int )
public abstract void removeLineItem( int )
// Get the array's size (without having to get the array, then
call .length).
public abstract int sizeOfLineItemArray()
最后,你可能注意到模式编译器生成了一个field:
public static final org.apache.xmlbeans.SchemaType type
you can use this field for access to a SchemaType instance that
represents the underlying schema type itself.This will be
covered in the last part of this tutorial.
注意:关于从模式生成类型的更多信息,请参考
http://xmlbeans.apache.org/docs/2.0.0/guide/conJavaTypesGenerate
dFromUserDerived.html,学习更多关于生成方法的信息,请参考
http://xmlbeans.apache.org/docs/2.0.0/guide/conMethodsForGenerat
edJavaTypes.html 同时,xmlbeans对于模式的内建类型如
xs:dateTime,xs:decimal等提供了它自己的java类型,关于更多信息,请
参考
http://xmlbeans.apache.org/docs/2.0.0/guide/conXMLBeansSupportBu
iltInSchemaTypes.html
使用生成的类型书写代码
为了增加一个line item到purchase order,你需要写一些代码。your
code will accept the existing order along with raw data for the
new item,然后增加item,返回更新完的xml。
在tutorials\gettingstarted\src创建一个POUpdater.java文件,代码如
下:
import java.io.*;
import java.math.*;
import org.apache.xmlbeans.*;
import org.openuri.easypo.*;
public class POUpdater
{
private static String addLineItem(File purchaseOrder, String
itemDescription,
String
perUnitOuncesString,
String itemPriceString,
String itemQuantityString)
{
// Bind the incoming XML to an XMLBeans type.
PurchaseOrderDocument poDoc = null;
try
{
poDoc = PurchaseOrderDocument.Factory.parse
(purchaseOrder);
} catch (XmlException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
return poDoc.toString();
}
}
至此,addLineItem方法把传入的xml和一个由模式产生的xmlbeans类型绑
定了。这个片段创建了一个方法,参数是一个代表purchase order的xml
文档的文件实例,一个包含raw data的字符(将要由新的增加的item包含
)。你将xml document(根节点和孩子节点)和purchaseorderdocument接
口绑定了。这个接口,像所有继承自xmlobject的类型一样,提供了一个
工厂类,来创建新的实例。工厂类提供了各种版本的parse方法,每一个
以不同的参数类型来接受xml源。
方法其他的代码,像下面所写出的,将传入的raw data转换成在创建新的
<line-item>元素时可以使用的类型。然后增加这个元素和设置孩子的值
。记住,在xmlbeans中,你得到表示顶层元素的类型是需要通过document
类型的----在这里,是通过getPurchaseOrder方法的。
BigDecimal perUnitOunces = new BigDecimal(perUnitOuncesString);
BigDecimal itemPrice = new BigDecimal(itemPriceString);
BigInteger itemQuantity = new BigInteger(itemQuantityString);
LineItem newItem = poDoc.getPurchaseOrder().addNewLineItem();
newItem.setDescription(itemDescription);
newItem.setPerUnitOunces(perUnitOunces);
newItem.setPrice(itemPrice);
newItem.setQuantity(itemQuantity);
下面是POUpdater.class的完整版本。
import java.io.*;
import java.math.*;
import org.apache.xmlbeans.*;
import org.apache.easypo.*;
public class POUpdater
{
public static void main(String[] args)
{
File poXmlFile = new File(args[0]);
String updatedPoXml = addLineItem(poXmlFile, args[1],
args[2],
args[3], args[4]);
System.out.println(updatedPoXml);
}
private static String addLineItem(File purchaseOrder, String
itemDescription,
String
perUnitOuncesString,
String itemPriceString,
String itemQuantityString)
{
PurchaseOrderDocument poDoc = null;
try
{
// Bind the incoming XML to an XMLBeans type.
poDoc = PurchaseOrderDocument.Factory.parse
(purchaseOrder);
} catch (XmlException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
// Convert incoming data to types that can be used in
accessors.
BigDecimal perUnitOunces = new BigDecimal
(perUnitOuncesString);
BigDecimal itemPrice = new BigDecimal(itemPriceString);
BigInteger itemQuantity = new BigInteger
(itemQuantityString);
// Add the new <line-item> element.
LineItem newItem = poDoc.getPurchaseOrder
().addNewLineItem();
newItem.setDescription(itemDescription);
newItem.setPerUnitOunces(perUnitOunces);
newItem.setPrice(itemPrice);
newItem.setQuantity(itemQuantity);
return poDoc.toString();
}
}
现在,像下面所示的通过命令来编译新的class。
javac -classpath %XMLBEANS_HOME%
\lib\xbean.jar;tutorials\gettingstarted\lib\easypo.jar
-d tutorials\gettingstarted\classes
tutorials\gettingstarted\src\POUpdater.java
编译之后,你可以使用下面的命令尝试运行。
java -cp tutorials\gettingstarted\classes;%XMLBEANS_HOME%
\lib\xbean.jar;tutorials\gettingstarted\lib\easypo.jar
POUpdater tutorials\gettingstarted\instances\easypo.xml "a
new item" 5.0 20.00 6
很明显,你需要一个模式来适用xmlbeans的这个方面。但是你可能发现当
你创建一个只有一些实例模式文件而不能编译得到这些java 类型----尽
量保持简单。
下面我们学习xml cursor
xml指针----由org.apache.xmlbeans.xmlcursor接口表达----被设计成作
为进入由编译模式文件生成的类型的javabeans-style的补充。例如,早
先前的代码中你增加了一个新的<line-item>元素,但是你对元素的去向
没有控制权。xmlbeans只是作为最后一个<line-item>元素进行插入。这
种由get/set对 提供的简单性是java类型的一种优势,但是如果你需要更
细的控制的话,如元素的顺序----你需要使用到xmlcursor了。
你可能会说xmlcursor提供了关于xml的模式不可知的观点。从一个指针的
观点看,the xml is a series of tokens.这些token被进行区分,被叫
做token类型,由org.apache.xmlbeans.xmlcursor.tokentype表示。
token类型包括start,end,attr和text.通过cursor,你可以通过在token之
间移动指针类导航遍历整个xml文档。
新增新的<line-item>元素,指针-style
在这个章节,你将插入一个同样的新<line-item>元素,但是这一次,需
要保证这个元素在list的右边,按字母顺序。为了方便,假设现在文档中
的元素已经按照字母顺序排好了。
创建一个新的方法叫做addLineItemWithCursor.这个方法是作为前面方法
的一个改变。在addLineItem方法中,你需要解析接受的xml文档,生成类
型,所以你的新的方法如下开始:
private static String addLineItemWithCursor(File purchaseOrder,
String itemDescription,
String perUnitOunces, String itemPrice, String itemQuantity)
{
PurchaseOrderDocument poDoc = null;
try
{
poDoc = PurchaseOrderDocument.Factory.parse
(purchaseOrder);
} catch (XmlException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
PurchaseOrderDocument.PurchaseOrder po =
poDoc.getPurchaseOrder();
}
接下来,你需要增加一些代码以此来检查字母顺序。为了这点,你可以使
用一个实现java.text.Collator的接口。java.text.RuleBaseCollator通
过对比words判断它是否需要放置在前面,正好可以完成你需要的。
RuleBasedCollator collator =
(RuleBasedCollator)Collator.getInstance(new Locale("en",
"US", ""));
现在可以开始cursor之旅了。对于继承自xmlobject的xmlbeans类型,你
都可以增加一个新的指针---包括表述你模式的,那些生成的类型。当你
通过newCursor方法来增加一个指针时,the cursor is created at the
beginning of the xml represented by the type on which you're
calling the method.例如,下面的代码将生成一个指针that
immediately precedes the XML represented by PurchaseOrder
XmlCursor cursor = po.newCursor();
换句话说,在这段代码之后,指针将立刻指到<purchase-order>元素的前
面--它将在元素的start token上。<purchase-order>元素有一个xmlns的
属性,所以,如果你调用cursor.toNextToken(),你就把cursor移到一个
代表属性的attr token上。
[img]http://xmlbeans.apache.org/images/tokentypes.jpg[/img]
但是现在,代码将cursor放在原处。你将调用另一个方法来得到缺省命名
空间的uri----当增加一个新的元素,你将需要使用到它。
String namespaceUri = cursor.namespaceForPrefix("");
开始真正的工作,你将得到一个代表<line-item>的数组元素,然后遍历
它,找到whose description belongs after the one you want to
insert.然后你的代码将新的元素放到找到的元素前面。
详细的说,你将存在的指针实例再分配到<line-item>元素的新指针
cursor = lineItem.newCursor();
然后,你将开始一个新的元素;将默认的命名空间的uri给新的元素。
cursor.beginElement("line-item", namespaceUri);
这个beginElement方法在指针所在的地方创建了一个新的元素,然后在新
元素的start 和end token之间放置指针。
最后,your code will populate the new <line-item>element with
child elements through further calls to beginElement and by
inserting text for the elements' values.
cursor.beginElement("description", namespaceUri);
cursor.insertChars(itemDescription);
cursor.toNextToken();
cursor.beginElement("per-unit-ounces", namespaceUri);
// ... and so on for the other children...
[img]http://xmlbeans.apache.org/images/tokentypes_newcursor.jpg[/img]
[img]http://xmlbeans.apache.org/images/beginelement.jpg[/img]
[img]http://xmlbeans.apache.org/images/after_beginelement.jpg[/img]
下面是方法的完整代码:
private static String addLineItemWithCursor(File purchaseOrder,
String itemDescription,
String perUnitOunces, String itemPrice, String itemQuantity)
{
PurchaseOrderDocument poDoc = null;
try
{
poDoc = PurchaseOrderDocument.Factory.parse
(purchaseOrder);
} catch (XmlException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
}
PurchaseOrderDocument.PurchaseOrder po =
poDoc.getPurchaseOrder();
// Set up the collator for alphabetizing.
RuleBasedCollator collator =
(RuleBasedCollator)Collator.getInstance(new Locale("en",
"US", ""));
XmlCursor cursor = po.newCursor();
// Get the document's URI so you can use it to insert.
String namespaceUri = cursor.namespaceForPrefix("");
// Get the array of <line-item> elements.
LineItem[] lineItems = po.getLineItemArray();
// Loop through the element array to discover where to
insert the new one.
for (int i = 0; i < lineItems.length; i++)
{
LineItem lineItem = lineItems[i];
// Find out if the new line item's description belongs
before the
// current line item's.
int comparison = collator.compare(itemDescription,
lineItem.getDescription());
// If the comparison returns -1, then insert the new
line item (and
// its children) before the current one.
if (comparison < 0)
{
cursor = lineItem.newCursor();
// Begin the new <line-item> element.
cursor.beginElement("line-item", namespaceUri);
// Begin the new <description> element and insert
its text value.
cursor.beginElement("description", namespaceUri);
cursor.insertChars(itemDescription);
// Move on and do the same for the other elements.
cursor.toNextToken();
cursor.beginElement("per-unit-ounces",
namespaceUri);
cursor.insertChars(perUnitOunces);
cursor.toNextToken();
cursor.beginElement("prices", namespaceUri);
cursor.insertChars(itemPrice);
cursor.toNextToken();
cursor.beginElement("quantity", namespaceUri);
cursor.insertChars(itemQuantity);
break;
}
}
// Speed the cursor's garbage collection and return the
updated XML.
cursor.dispose();
return poDoc.toString();
}
在测试之前,你将更改你的main方法,让其调用addLineItemCursor而不
是addLineItem。
public static void main(String[] args)
{
File poXmlFile = new File(args[0]);
// String updatedPoXml = addLineItem(poXmlFile, args[1],
args[2],
// args[3], args[4]);
String updatedPoXml = addLineItemWithCursor(poXmlFile, args
[1], args[2],
args[3], args[4]);
System.out.println(updatedPoXml);
}
最后,在编译之前,你需要增加2个import语句。
import java.text.*;
import java.util.*;