XML的方法–第3部分– JAXB

如果您还记得第1部分第2部分中的内容 ,那么我将介绍使用 Pete的Perfect Pizza (具有丰富创意的比萨公司)的 残酷场景来解析XML消息的不同方法 。 在这个故事中,您是Pete's的雇员,因此被要求实现一个从前台向厨房发送订单的系统,然后您想到了使用XML的想法。 您刚刚使SAX Parser起作用,但是Pete走向全球,在世界各地开设厨房,并通过Internet接受订单。 他雇用了一些顾问,他们提出了扩展您舒适的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已经存在了一段时间,因此必须有一些有用的框架。 经过更多的Google搜索之后,您发现JAXB并意识到……

JAXB或用于XML绑定Java体系结构使用JAXB绑定编译器xjc将XML模式隐藏到一堆相关的Java类中。 这些定义了以类型安全的方式访问XML元素,属性和其他内容所需的类型。 本博客不是涵盖JAXB内容的教程,可以在Oracle,OracleGlassfish,Project Metro和JAXB页面上找到 ,它也包括一个教程 。 除了说解析或解组XML的关键思想外,您还可以使用xjc编译器编译Java类,然后将这些类与JAXB API结合使用来获取XML元素和属性。

在将任何XML模式用于Java类编译器时,最巧妙的方法是将所有模式及其编译的类放入单独的JAR文件中。 您可以将它们与应用程序的源代码混合在一起,但是通常会使代码库模糊,从而使维护更加困难。 在创建JAXB 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-jaxb</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Jaxb for Pete's Perfect Pizza</name>
 <dependencies>
  <dependency>
   <groupId>javax.xml.bind</groupId>
   <artifactId>jaxb-api</artifactId>
   <version>2.0</version>
  </dependency>
 </dependencies>
 <build>
  <plugins>
   <plugin>
    <groupId>com.sun.tools.xjc.maven2</groupId>
    <artifactId>maven-jaxb-plugin</artifactId>
    <executions>
     <execution>
      <goals>
       <goal>generate</goal>
      </goals>
     </execution>
    </executions>
    <configuration>
     <generatePackage>com.captaindebug.jaxb</generatePackage>
     <includeSchemas>
      <includeSchema>**/*.xsd</includeSchema>
     </includeSchemas>
     <strict>true</strict>
     <verbose>true</verbose>
    </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 ,您已经创建了JAXB JAR文件,剩下要做的就是探索其工作方式,如下面的JUnit测试所示:

@Test

  public void testLoadPizzaOrderXml() throws JAXBException, IOException {



    InputStream is = loadResource('/pizza-order1.xml');



    // Load the file

    JAXBContext context = JAXBContext.newInstance(PizzaOrder.class);

    Unmarshaller um = context.createUnmarshaller();

    PizzaOrder pizzaOrder = (PizzaOrder) um.unmarshal(is);



    String orderId = pizzaOrder.getOrderID();

    assertEquals('123w3454r5', orderId);



    // Check the customer details...

    CustomerType customerType = pizzaOrder.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 = pizzaOrder.getPizzas();

    List<PizzaType> pizzasOrdered = pizzas.getPizza();



    assertEquals(3, pizzasOrdered.size());



    // Check the pizza order...

    for (PizzaType pizza : pizzasOrdered) {



      PizzaNameType 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 InputStream loadResource(String filename) throws IOException {



    InputStream is = getClass().getResourceAsStream(filename);

    if (is == null) {

      throw new IOException('Can't find the file: ' + filename);

    }



    return is;

  }

上面的代码可能看起来很长而且很复杂,但实际上仅包括三个步骤:

  1. 将测试文件转换为输入流。 然后可以由JAXB API处理。
  2. 创建一个JAXB上下文及其关联的解组器。 然后将其用于读取XML并将其转换为元素和属性对象(如果有)
  3. 使用返回的类列表来测试内容是否符合我们的期望(到目前为止,这是最大的一步)。

一旦对JAXB的大量用法感到满意,就可以将其添加到厨房XML解析器代码中,并将其分布到Pete的许多披萨厨房中。

使用JAXB之类的框架的优点之一是,如果对架构进行了更改,那么合并这些更改所需要做的就是使用XJC重新编译,然后相应地修复客户端代码。 这似乎有点让人头疼,但是与尝试重新制作SAX解析器相比,它要头痛得多。 不利的一面是,JAXB因运行缓慢而受到批评,但我从来没有遇到太多问题。 从理论上讲,它将使用比SAX更多的内存–这可能是正确的,也可能不是。 它确实构建了类集,但同样会重复一些SAX ContentHandler派生的类。

应当记住,JAXB并不是采用这种方法的唯一工具,为了演示这一点,我的下一个博客也讲述了同样的故事,但是使用XMLBeans。

可从GitHub上获得源代码:

git://github.com/roghughe/captaindebug.git

继续阅读本系列的第4部分

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


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值