xmlbeans
但是,请稍等...我不是在上一个博客中这么说吗? Déjàvu? 今天的博客是我的JAXB博客的替代现实版本,因为情况仍然相同,但是解决方案有所变化。 我将不研究JAXB,而是研究XMLBeans。
因此,Pete聘请了一些顾问,他们提出了扩展您舒适的XML消息的计划,并且他们已经使用模式对其进行了指定。 他们还通过添加自己的客户模式之一来增强了您的信息。 结果是以下XSD文件进入了您的收件箱,您需要忙碌……
<?xml version='1.0' encoding='UTF-8'?>
<!-- edited with XMLSpy v2011 sp1 (http://www.altova.com) by Roger Hughes (Marin Solutions Ltd) -->
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:ppp='http://www.petesperfectpizza.com' xmlns:cust='http://customer.dets' targetNamespace='http://www.petesperfectpizza.com' elementFormDefault='qualified' attributeFormDefault='unqualified' version='1.00'>
<!-- Import the Namespaces required -->
<xs:import namespace='http://customer.dets' schemaLocation='customer.xsd'/>
<!-- The Root Node -->
<xs:element name='PizzaOrder'>
<xs:annotation>
<xs:documentation>A wrapper around the customer and the pizza order</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name='orderID' type='ppp:CorrelationIdentifierType'/>
<xs:element name='date' type='ppp:DateType'/>
<xs:element name='time' type='ppp:TimeType'/>
<xs:element name='Customer' type='cust:CustomerType'/>
<xs:element ref='ppp:pizzas'/>
</xs:sequence>
</xs:complexType>
</xs:element>
<!-- The Pizza Order-->
<xs:element name='pizzas'>
<xs:annotation>
<xs:documentation>This is a list of pizzas ordered by the customer</xs:documentation>
</xs:annotation>
<xs:complexType>
<xs:sequence>
<xs:element name='pizza' type='ppp:PizzaType' minOccurs='1' maxOccurs='unbounded'/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name='PizzaType'>
<xs:sequence>
<xs:element name='name' type='ppp:PizzaNameType'>
<xs:annotation>
<xs:documentation>The type of pizza on the menu</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name='base' type='ppp:BaseType'>
<xs:annotation>
<xs:documentation>type of base</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element name='quantity' type='ppp:QuantityType'>
<xs:annotation>
<xs:documentation>quantity of pizzas</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
<xs:simpleType name='PizzaNameType'>
<xs:restriction base='xs:token'>
<xs:enumeration value='Margherita'>
<xs:annotation>
<xs:documentation>Plain and Simple</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value='Marinara'>
<xs:annotation>
<xs:documentation>Garlic Pizza...</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value='Prosciutto e Funghi'>
<xs:annotation>
<xs:documentation>Ham and Musheroom</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value='Capricciosa'>
<xs:annotation>
<xs:documentation>with an egg</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='BaseType'>
<xs:restriction base='xs:token'>
<xs:enumeration value='thin'>
<xs:annotation>
<xs:documentation>thin base traditional</xs:documentation>
</xs:annotation>
</xs:enumeration>
<xs:enumeration value='thick'>
<xs:annotation>
<xs:documentation>Thick base</xs:documentation>
</xs:annotation>
</xs:enumeration>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='QuantityType'>
<xs:restriction base='xs:nonNegativeInteger'/>
</xs:simpleType>
<xs:simpleType name='CorrelationIdentifierType'>
<xs:restriction base='xs:token'>
<xs:maxLength value='44'/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='DateType'>
<xs:annotation>
<xs:documentation>The date is in the Common Era (minus sign in years is not permitted)</xs:documentation>
</xs:annotation>
<xs:restriction base='xs:date'>
<xs:pattern value='\d{4}-\d{2}-\d{2}'/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='TimeType'>
<xs:annotation>
<xs:documentation>The time zone although not included UTC is implied</xs:documentation>
</xs:annotation>
<xs:restriction base='xs:time'>
<xs:pattern value='\d{2}:\d{2}:\d{2}(\.\d+)?'/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
<?xml version='1.0' encoding='UTF-8'?>
<!-- edited with XMLSpy v2011 sp1 (http://www.altova.com) by Roger Hughes (Marin Solutions Ltd) -->
<xs:schema xmlns:xs='http://www.w3.org/2001/XMLSchema' xmlns:cust='http://customer.dets' targetNamespace='http://customer.dets' elementFormDefault='qualified' attributeFormDefault='unqualified'>
<xs:element name='Customer' type='cust:CustomerType'>
<xs:annotation>
<xs:documentation>Generic Customer Definition</xs:documentation>
</xs:annotation>
</xs:element>
<xs:complexType name='CustomerType'>
<xs:sequence>
<xs:element name='name' type='cust:NameType'/>
<xs:element name='phone' type='cust:PhoneNumberType'/>
<xs:element name='address' type='cust:AddressType'/>
</xs:sequence>
</xs:complexType>
<xs:complexType name='NameType'>
<xs:sequence>
<xs:element name='firstName' type='cust:FirstNameType'/>
<xs:element name='lastName' type='cust:LastNameType'/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name='FirstNameType'>
<xs:annotation>
<xs:documentation>The Customer's first name</xs:documentation>
</xs:annotation>
<xs:restriction base='xs:token'>
<xs:maxLength value='16'/>
<xs:pattern value='.{1,16}'/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='LastNameType'>
<xs:annotation>
<xs:documentation>The Customer's surname</xs:documentation>
</xs:annotation>
<xs:restriction base='xs:token'>
<xs:pattern value='.{1,48}'/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name='AddressType'>
<xs:sequence>
<xs:element name='houseNumber' type='cust:HouseNumberType'/>
<xs:element name='street' type='cust:AddressLineType'/>
<xs:element name='town' type='cust:AddressLineType' minOccurs='0'/>
<xs:element name='area' type='cust:AddressLineType' minOccurs='0'/>
<xs:element name='postCode' type='cust:PostCodeType'/>
</xs:sequence>
</xs:complexType>
<xs:simpleType name='HouseNumberType'>
<xs:annotation>
<xs:documentation>The house number</xs:documentation>
</xs:annotation>
<xs:restriction base='xs:nonNegativeInteger'/>
</xs:simpleType>
<xs:simpleType name='AddressLineType'>
<xs:annotation>
<xs:documentation>A line of an address</xs:documentation>
</xs:annotation>
<xs:restriction base='xs:token'>
<xs:pattern value='.{1,100}'/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='PhoneNumberType'>
<xs:restriction base='xs:token'>
<xs:maxLength value='18'/>
<xs:pattern value='.{1,18}'/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name='PostCodeType'>
<xs:restriction base='xs:token'>
<xs:maxLength value='10'/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
您意识到,使用这种级别的复杂性,您将在很长一段时间内都在使用SAX,并且可能还会犯一些错误。 必须有更好的方法吧? 毕竟XML已经存在了一段时间,因此围绕它的大多数框架都可能有用。 经过更多的谷歌搜索之后,您发现XMLBeans并意识到……
XMLBeans使用特殊的编译器将XML模式转换为一堆相关的Java类,这些类定义了以类型安全的方式访问XML元素,属性和其他内容所需的类型。 本博客不是涵盖XMLBeans来龙去脉的教程,可以在Apache上找到该教程,只是说解析或解组XML的关键思想是先使用XMLBeans编译Java类,然后再使用您的应用程序中的那些类。
在将任何XML模式用于Java类编译器时,最巧妙的方法是将所有模式和编译器放在单独的JAR文件中。 您可以将它们与应用程序的源代码混合在一起,但是通常会使代码库模糊,从而使维护更加困难。 在创建XMLBeans JAR文件时,您可能会想到一个类似于以下内容的POM文件:
<project xmlns='http://maven.apache.org/POM/4.0.0' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xsi:schemaLocation='http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd'>
<modelVersion>4.0.0</modelVersion>
<groupId>com.captaindebug</groupId>
<artifactId>xml-tips-xmlbeans</artifactId>
<packaging>jar</packaging>
<version>1.0-SNAPSHOT</version>
<name>XML Beans for Pete's Perfect Pizza</name>
<dependencies>
<dependency>
<groupId>org.apache.xmlbeans</groupId>
<artifactId>xmlbeans</artifactId>
<version>2.4.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xmlbeans-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>xmlbeans</goal>
</goals>
</execution>
</executions>
<inherited>true</inherited>
<configuration>
<schemaDirectory>src/main/resources</schemaDirectory>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
……这很简单。 因此,回到Pete的Perfect Pizza ,您已经创建了XMLBeans JAR文件,剩下要做的就是探索其工作方式,如以下JUnit测试所示:
public class PizzaXmlBeansTest {
private PizzaOrderDocument instance;
@Test
public void testLoadPizzaOrderXml() throws IOException, XmlException {
String xml = loadResource('/pizza-order1.xml');
instance = PizzaOrderDocument.Factory.parse(xml);
PizzaOrder order = instance.getPizzaOrder();
String orderId = order.getOrderID();
assertEquals('123w3454r5', orderId);
// Check the customer details...
CustomerType customerType = order.getCustomer();
NameType nameType = customerType.getName();
String firstName = nameType.getFirstName();
assertEquals('John', firstName);
String lastName = nameType.getLastName();
assertEquals('Miggins', lastName);
AddressType address = customerType.getAddress();
assertEquals(new BigInteger('15'), address.getHouseNumber());
assertEquals('Credability Street', address.getStreet());
assertEquals('Any Town', address.getTown());
assertEquals('Any Where', address.getArea());
assertEquals('AW12 3WS', address.getPostCode());
Pizzas pizzas = order.getPizzas();
PizzaType[] pizzasOrdered = pizzas.getPizzaArray();
assertEquals(3, pizzasOrdered.length);
// Check the pizza order...
for (PizzaType pizza : pizzasOrdered) {
PizzaNameType.Enum pizzaName = pizza.getName();
if ((PizzaNameType.CAPRICCIOSA == pizzaName) || (PizzaNameType.MARINARA == pizzaName)) {
assertEquals(BaseType.THICK, pizza.getBase());
assertEquals(new BigInteger('1'), pizza.getQuantity());
} else if (PizzaNameType.PROSCIUTTO_E_FUNGHI == pizzaName) {
assertEquals(BaseType.THIN, pizza.getBase());
assertEquals(new BigInteger('2'), pizza.getQuantity());
} else {
fail('Whoops, can't find pizza type');
}
}
}
private String loadResource(String filename) throws IOException {
InputStream is = getClass().getResourceAsStream(filename);
if (is == null) {
throw new IOException('Can't find the file: ' + filename);
}
return toString(is);
}
private String toString(InputStream is) throws IOException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
copyStreams(is, bos);
return bos.toString();
}
private void copyStreams(InputStream is, OutputStream os) throws IOException {
byte[] buf = new byte[1024];
int c;
while ((c = is.read(buf, 0, 1024)) != -1) {
os.write(buf, 0, c);
os.flush();
}
}
}
上面的代码可能看起来很长而且很复杂,但是实际上仅包括三个步骤:首先,将测试文件转换为合适的类型,例如String或InputStream (XMLBeans可以处理几种不同的输入类型)。 然后,使用嵌套的Factory类处理将XML源转换为文档对象的过程。 最后,使用返回的文档对象来测试您的结果是否符合预期(这是迄今为止最大的一步)。 一旦对XMLBeans的大量用法感到满意,就可以将其添加到Pete的Perfect Pizza厨房XML解析器代码中,并将其分布到Pete的许多Pizza厨房中。
使用XMLBeans之类的框架的优点之一是,如果对模式进行了更改,那么合并这些更改所需的全部就是重新编译,并相应地修复客户端代码。 这似乎有点令人头疼,但与尝试重新制作SAX解析器相比,它要头痛得多。 不利的一面是,人们批评XMLBeans的速度很慢,但是我从来没有遇到太多问题。 从理论上讲,它将使用SAX的更多内存–这可能是正确的,也可能是不正确的–它确实构建了类集,但随后又重复了一些SAX ContentHandler派生的类。
最后,应该指出的是,自2009年以来没有发布过XMLBeans,根据您的观点,这可能不是好事,尽管我应该强调,就我所知,此代码当然不是多余的知识,它仍然广泛用于许多大型项目。
可从GitHub上获得源代码:
git://github.com/roghughe/captaindebug.git
参考: Captain Debug博客博客上的JCG合作伙伴 Roger Hughes的XML方法–第4部分– XMLBeans 。
翻译自: https://www.javacodegeeks.com/2012/07/approaches-to-xml-part-4-xmlbeans.html
xmlbeans