XML的方法–第4部分– XMLBeans

如果您从第1部分第2部分第3 部分中还记得,我将介绍使用 Pete的Perfect Pizza (具有重大创意的比萨公司)的 残酷场景来解析XML消息的不同方法 。 在这个故事中,您是Pete's的雇员,因此被要求实现一个从前台向厨房发送订单的系统,然后您想到了使用XML的想法。 您刚刚使SAX Parser起作用,但是Pete走向全球,在世界各地开设厨房,并通过Internet接受订单。

但是,请稍等...我不是在上一个博客中这么说吗? 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类,这些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();

    }

  }



}

上面的代码可能看起来很长而且很复杂,但实际上仅包括三个步骤:首先,将测试文件转换为合适的类型,例如StringInputStream (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

参考: XML的方法–第4部分–来自JCG合作伙伴 Roger Hughes的Captain Debug的Blog博客中的XMLBeans


翻译自: https://www.javacodegeeks.com/2012/07/approaches-to-xml-part-4-xmlbeans.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值