Skip navigation.

XML Beans

by David Bau
03/03/2003

Today's programmers face a flood of data. Servers routinely exchange their terabytes with the gigabytes of capacity that reside cheaply, plentifully, ubiquitously, in the little boxes on every one of our desks. Copper, air, and glass are awash with tens and thousands of megabits per second, connecting us all on one enormous, planetary network.

In this world, interoperability is the king of all programming problems. Programs need efficiency and robustness to withstand the never-ending rush of data, errors, and attacks, and programmers must be flexible and quick to adapt to the ever-rising, ever-shifting tide of new forms of data.

The ubiquitous atomic form - the H2O for the expanding ocean of data - is XML. And of course XML implies other technologies. The XML seas are charted using XML Schema, the W3C standard for describing data formats. XML Query is the best new language for plotting a course, specifying both transformations and paths through the data. And WSDL and SOAP are the leading nautical conventions: they bring together a set of best practices, conventions, and rules for exchanging and binding XML.

There has been much previous work on XML in Java. The Java community already enjoys an alphabet soup of XML libraries, standards, and products, a multifunctional toolbox for parsing, validating, transmitting, and binding XML for a variety of situations. Different XML tasks can be solved using different tools.

XML Beans is a new approach for our data-dominated world. The focus of XML Beans is not on one specific XML technology, but on making data-oriented programming possible in Java. XML Beans is not a toolset for "solving XML," but for "solving data problems using XML." This means that all the basic data-oriented XML technologies are integrated in a single unified package: parsing, streaming, validation, schema, binding, messaging, and query. When using XML Beans, you don’t need to worry about the technology. You can depend on the fact that all the relevant technologies are available, and you just think about the problem you are addressing.

I think you will find the tool complete and easy to use, even in the beta of its very first release.

So what are XML Beans? What is data-oriented programming?

We will start to answer those questions in this article with a simple example.

The Data

In data-oriented programming, you start by understanding your critical data, and then you build your programs around it. We will build a web service by following these steps:

  1. The Data - examining the data that we’re dealing with
  2. The Schema - codifying our knowledge of the data in an XML schema.
  3. The Web Service - building a client and a server to exchange the data.
  4. The Contract - examining the WSDL contract and SOAP envelope.
  5. The Program - writing the logic of the program.
The key steps are 1 and 2, understanding your data - once you've done that, everything else is very easy with XML Beans and Workshop. Let us take a look at some data.


  <m:order xmlns:m="http://scopetrade.com/order-schema">
     <m:note>Sam - please remember to include the eyepiece case!</m:note>
     <m:customer>Mary Johnson</m:customer>
     <m:ship-to>
       <m:street>410 Mill Creek Road</m:street>
       <m:street>Suite 423</m:street>
       <m:street>ATTN: Mary Johnson</m:street>
       <m:city>Philadelphia</m:city>
       <m:state>PA</m:state>
     </m:ship-to>
     <m:line-item>
       <m:description>XT 10 Telescope</m:description>
       <m:quantity>1</m:quantity>
       <m:price>789.00</m:price>
     </m:line-item>
     <m:line-item>
       <m:description>Plossl 25mm eyepiece</m:description>
       <m:quantity>2</m:quantity>
       <m:price>27.99</m:price>
       <m:note>We need cases for these.</m:note>
     </m:line-item>
  </m:order>

This is an example of a simplified purchase order document. It has any number of line-items, a customer address, and some notes.

Documents like this are easy to create - you could type one in by hand, or write a simple program to print one out using ordinary strings. And they are also easy for people to read. That is why XML is such a wonderful starting point for broad interoperability. But if we want to have a hope of having any robustness when we exchange these kinds of documents in an automated way, we need to be able to describe exactly what the document shape is expected to be.

For example, in our example document above there are two <note> tags. If Mary Johnson were to inspect the XML directly, she would see the notes no matter where they appeared. However, when the document is processed by a program, the program needs to be aware of where it can expect to find notes and where it is appropriate to insert notes. Programs also need to know details such as "a <quantity> is an integer" and "a <price> is a fractional number". This is where XML Schema comes into play.

The Schema

Here is a complete schema document describing the document shape above.

  <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
             xmlns:tns="http://scopetrade.com/order-schema"
             elementFormDefault="qualified"
             targetNamespace="http://scopetrade.com/order-schema">

    <xs:element name="order">
      <xs:complexType>
        <xs:sequence>
          <xs:element name="note" type="xs:string" minOccurs="0"/>
          <xs:element name="customer" type="xs:string"/>
          <xs:element name="ship-to" type="tns:address"/>
          <xs:element name="line-item" type="tns:product-and-qty"
                      minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>
      </xs:complexType>
    </xs:element>

    <xs:complexType name="address">
      <xs:sequence>
        <xs:element name="street" type="xs:string" maxOccurs="3"/>
        <xs:element name="city" type="xs:string"/>
        <xs:element name="state" type="tns:two-letter-state"/>
      </xs:sequence>
     </xs:complexType>

    <xs:complexType name="product-and-qty">
      <xs:sequence>
        <xs:element name="description" type="xs:string"/>
        <xs:element name="quantity" type="xs:int"/>
        <xs:element name="price" type="xs:double"/>
        <xs:element name="note" type="xs:string" minOccurs="0"/>
      </xs:sequence>
    </xs:complexType>

    <xs:simpleType name="two-letter-state">
      <xs:restriction base="xs:string">
        <xs:length value="2"/>
       </xs:restriction>
    </xs:simpleType>
  </xs:schema>

What does the schema file say?

1.  All its definitions are in the "http://scopetrade.com/order-schema" namespace. It defines one global element called "order", two complex types named "address" and "product-and-qty", and one simple type named "two-letter-state". All these type and element names are part of the given namespace.

  <xs:schema ...
             targetNamespace="http://scopetrade.com/order-schema">

2.  Global elements are used to define how documents may begin. Here, documents begin with an <order> tag.

  <xs:element name="order">

3.  It defines an anonymous nested complex type within the <order> element. The nested complex type definition is a sequence starting with an optional <note> with a string, followed by a <customer>, then a <ship-to>, and finally any number of <line-item> elements. The type of <ship-to> is an "address" described in the same namespace, and the type of <line-item> is a "product-and-qty" in the same namespace. Note that the complex type definition is nested and not given a name. That means this complex type cannot be reused elsewhere in the schema.

    <xs:complexType>
      <xs:sequence>
        <xs:element name="note" type="xs:string" minOccurs="0"/>
        <xs:element name="customer" type="xs:string"/>
        <xs:element name="ship-to" type="tns:address"/>
        <xs:element name="line-item" type="tns:product-and-qty"
                    minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>

4.  The schema defines named complex types "address" and "product-and-qty". These global type definitions can be in other parts of the schema. For example, the <ship-to> tag is defined as an "address" and one could modify the schema to add a <ship-from> tag whose type is also "address". Named types are reusable.

  <xs:complexType name="address">...
  <xs:complexType name="product-and-qty">...

5.  Finally, it also defines a simple type for state codes by adding a restriction facet rule to the ordinary string type. A two-letter-state must have two characters.

  <xs:simpleType name="two-letter-state">
    <xs:restriction base="xs:string">
      <xs:length value="2"/>
     </xs:restriction>
  </xs:simpleType>

Sometimes you will build your own schema, and sometimes you will begin with a standard schema that already exists. There may be standardized schemas for various types of common documents in your industry, and in order to interoperate you will want to conform to the standard schemas exactly. If you are writing your own schemas, you can use a tool such as XML Spy (which is included with Workshop).

As with any language, there are many powerful features in XML Schema that can’t be covered in just one page. It’s easy to get started with simple schemas by working from examples such as the one above, but as you get into more details, you will want to understand more of the language.

I suggest picking up one of the many excellent books written on the XML Schema language. One book that I think does a good job at covering all the details is by Patricia Walmsley, "Definitive XML Schema", published by Addison-Wesley.

The XML Beans

Once we have the schema, we’re almost done! The rest of the steps are easy.

We now want to get ready to use the schema from within Java. To do this, we need to compile our schema into XML Beans. Each XML Bean is a Java interface that corresponds to an XML Schema type or element.

With Weblogic Workshop, XML Schema can be compiled by simply dropping the schema file into a "Schema" project in our application. Any schemas in that folder will be compiled into XML Beans that can be used from Java.

Outside of the Workshop IDE, compilation of XML Beans from XML Schema can be also done via a command line script or from an ant task.

Let us take a look at the five Java interfaces that are compiled from the Schema above.

  // A whole <order> document, has methods like getOrder()
  com.scopetrade.orderSchema.OrderDocument

  // The complex type for the contents of an <order>, has getShipTo()
  com.scopetrade.orderSchema.Order

  // The address complex type, has getCity()
  com.scopetrade.orderSchema.Address

  // The product-and-qty complex type, has getDescription()
  com.scopetrade.orderSchema.ProductAndQty

  // The two-letter-state simple type, has stringValue()
  com.scopetrade.orderSchema.TwoLetterState

As you can see, the XML namespace "http://scopetrade.com/order-schema" compiles into the Java package "com.scopetrade.orderSchema", and every complex and simple type translates into a corresponding Java interface. For example, the XML "address" type has compiled to the Java "Address" interface. The XML global element "order" has been compiled to the Java "OrderDocument" interface. The methods on the Java interfaces provide easy, strongly-typed access to parts of the XML data.

The conversion of schema constraints into strongly-typed Java methods such as getShipTo() is called binding, and it dramatically simplifies use of XML within Java. Binding XML types to Java types using XML Beans brings several advantages.

  1. XML Bean methods present the XML structure. This means that as a programmer, you no longer need to remember all the details of the XML schema: the Java classes present the schema structure for you. For example, if an order contains a <ship-to> element, your order object will have a getShipTo() methods. This is single feature is a huge payoff for large schemas with hundreds or thousands of different kinds of XML elements and types.
  2. XML Beans parse XML types into Java types. For example, the method lineItem.getQuantity() returns a Java int. You no longer need to write your own string parsing code, or worry about the details of the interpreting the syntax of all of the XML Schema built-in data types. XML Beans meticulously and efficiently parses all the built-in XML types such as dates, numbers and enumerations, and you don’t need to worry about trivial but important details such as when to collapse XML spaces, or how to parse a time zone attached to a Gregorian Month.
  3. XML Beans provide robust interoperability through XML validity. On one hand, XML Beans helps you avoid creating invalid XML. For example, if you call addNewShipTo() on an XML Bean Order object, the <ship-to> tag will be added in the proper place within the XML according to the schema. And on the other hand, XML Beans help you protect programs against invalid XML. Each XML Bean has a validate() method that allows you to enforce or verify all XML Schema constraints in any part of the XML document. Applying a one-time validation is easier, more robust, and more efficient than checking the validity of every field by hand when processing XML deep inside your application.
  4. XML Beans add XML types to the Java type system. Bringing XML types into Java means that the Java language helps enforce XML type correctness. It also means that the typing of your XML-manipulation code in Java is self-describing. For example, if you have a method that processes a line-item, you can have a method signature that looks like "void process(ProductAndQty item", and there is little chance that the method will accidentally be confused with a method that processes a different type of XML data.
  5. XML Beans are still XML and can be queried or fully traversed. Although XML Beans translate XML types into Java types, they do not lose any of the features of the original XML. For example, XML Beans provide methods for XPath and XQuery on the XML data. And it is possible to manipulate the entire XML infoset including unbound parts of the XML such as XML comments and processing instructions.
Let us now use these XML Beans that we have compiled to build a web service.

The Web Service

Here is the complete source code for a JWS web service definition for a server that exchanges the XML Beans inside SOAP messages.

  import com.scopetrade.orderSchema.*;

  public class OrderSystem
  {
     /**
      * @jws:operation
      */
     public String submitOrder(OrderDocument doc)
     {
        return "Got it."
     }
  }

That’s it! We now have a working service that exchanges our XML.

As you can see, to build a web service that exchanges <order> documents, all we need to do is write a JWS operation that accepts or returns an OrderDocument XML Bean.

Since an OrderDocument is an XML document that starts with an <order> element, the JWS compiler knows exactly what kind of XML data intend to process with this service. All the work of handling SOAP and WSDL for this kind of payload is done automatically for you. Let’s examine the details of these wire contracts next.

The Contract

The dominant conventions for implementing and interoperating with web services are WSDL and SOAP, so by default our web service accepts SOAP messages in a conventional "document-literal" style and advertises the precise contract using a WSDL file that can be downloaded.

If we view the OrderSystem.jws service with a web browser, we see a console with a "Test XML" tab that lets us submit the body of SOAP messages. The form provides us with sample XML based on the schema that we can send into our service. The sample document will have empty places where we are supposed to fill in actual data. Since we have a sample bit of data already, we can just type or paste it in.

Here is an example SOAP message in its entirety. (Note that the Test XML form just asks you to supply the contents of the SOAP Body - the envelope and body tags themselves are automatically supplied for you.)

<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/">
  <SOAP:Body>
    <ns1:submitOrder xmlns:ns1="http://www.openuri.org/">
      <m:order xmlns:m="http://scopetrade.com/order-schema">
        <m:note>Sam - please remember...</m:note>
        <m:customer>Mary Johnson</m:customer>
         <m:ship-to>
           <m:street>410 Mill Creek Road</m:street>
           <m:street>Suite 423</m:street>
           <m:street>ATTN: Mary Johnson</m:street>
           <m:city>Philadelphia</m:city>
           <m:state>PA</m:state>
         </m:ship-to>
         <m:line-item>
           <m:description>XT 10 Telescope</m:description>
           <m:quantity>1</m:quantity>
           <m:price>789.00</m:price>
         </m:line-item>
         <m:line-item>
           <m:description>Plossl 25mm eyepiece</m:description>
           <m:quantity>2</m:quantity>
           <m:price>27.99</m:price>
           <m:note>We need cases for these.</m:note>
         </m:line-item>
       </m:order>
    </ns1:submitOrder>
  </SOAP:Body>
</SOAP:Envelope>

Notice that, as required by the SOAP spec, the messages accepted by our web service are contained in a special SOAP envelope consisting of the <Envelope> and <Body> tags.

Also notice that we have followed the convention of putting the <order> payload inside another tag named <submitOrder> which corresponds to the web service operation name. This extra tag allows you to define more than one operation that accepts an <order> if you wish: the messages will be routed to the proper method based on this top tag.

Our web service provides a downloadable WSDL contract describing the public contract for communicating with the service. The WSDL is reproduced in a small font size on the following pages.

 <?xml version="1.0" encoding="utf-8"?>

 <definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
              xmlns:s="http://www.w3.org/2001/XMLSchema"
              xmlns:s0="http://www.openuri.org/"
              xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
              targetNamespace="http://www.openuri.org/">

   <types>

     <s:schema attributeFormDefault="qualified" elementFormDefault="qualified"
               targetNamespace="http://www.openuri.org/">
       <s:element name="submitOrder">
         <s:complexType>
           <s:sequence>
             <s:element xmlns:xbean="http://scopetrade.com/order-schema"
                        ref="xbean:order" minOccurs="1" maxOccurs="1"/>
           </s:sequence>
         </s:complexType>
       </s:element>
       <s:element name="submitOrderResponse">
         <s:complexType>
           <s:sequence>
             <s:element minOccurs="0" maxOccurs="1" name="submitOrderResult"
                        type="s:string"/>
           </s:sequence>
         </s:complexType>
       </s:element>
     </s:schema>

     <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:tns="http://scopetrade.com/order-schema"
                elementFormDefault="qualified"
                targetNamespace="http://scopetrade.com/order-schema">

       <xs:element name="order">
         <xs:complexType>
           <xs:sequence>
             <xs:element name="note" type="xs:string" minOccurs="0"/>
             <xs:element name="customer" type="xs:string"/>
             <xs:element name="ship-to" type="tns:address"/>
             <xs:element name="line-item" type="tns:product-and-qty" minOccurs="0"
                         maxOccurs="unbounded"/>
           </xs:sequence>
         </xs:complexType>
       </xs:element>

       <xs:complexType name="address">
         <xs:sequence>
           <xs:element name="street" type="xs:string" maxOccurs="3"/>
           <xs:element name="city" type="xs:string"/>
           <xs:element name="state" type="tns:two-letter-state"/>
         </xs:sequence>
       </xs:complexType>

       <xs:complexType name="product-and-qty">
         <xs:sequence>
           <xs:element name="description" type="xs:string"/>
           <xs:element name="quantity" type="xs:int"/>
           <xs:element name="price" type="xs:double"/>
           <xs:element name="note" type="xs:string" minOccurs="0"/>
         </xs:sequence>
       </xs:complexType>

       <xs:simpleType name="two-letter-state">
         <xs:restriction base="xs:string">
           <xs:length value="2"/>
         </xs:restriction>
       </xs:simpleType>
     </xs:schema>
   </types>

   <message name="submitOrderSoapIn">
     <part name="parameters" element="s0:submitOrder"/>
   </message>
   <message name="submitOrderSoapOut">
     <part name="parameters" element="s0:submitOrderResponse"/>
   </message>

   <portType name="OrderSystemSoap">
     <operation name="submitOrder">
       <input message="s0:submitOrderSoapIn"/>
       <output message="s0:submitOrderSoapOut"/>
     </operation>
   </portType>

   <binding name="OrderSystemSoap" type="s0:OrderSystemSoap">
     <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
     style="document"/>
     <operation name="submitOrder">
       <soap:operation soapAction="http://www.openuri.org/submitOrder"
       style="document"/>
       <input>
         <soap:body use="literal"/>
       </input>
       <output>
         <soap:body use="literal"/>
       </output>
     </operation>
   </binding>

   <service name="OrderSystem">
     <port name="OrderSystemSoap" binding="s0:OrderSystemSoap">
       <soap:address location="http://lightbox:7001/samples/OrderSystem.jws"/>
     </port>
   </service>
 </definitions>

As you can see, WSDL contract descriptions can be long, and usually you will not need or want to worry about all the details and conventions of WSDL. The JWS compiler has handled the WSDL conventions for you.

For example, the <schema> definition describing our <order> documents has automatically been included in the <types> section of the WSDL so that other services can understand the complete details of the wire contract. In addition, another <schema> section has been automatically generated to define types that conform to the enveloping conventions of SOAP and WSDL.

I have mentioned that the contract implemented by our example JWS service conforms to two conventions:

  1. The SOAP standard is used for enveloping the Web Service message. This means that you can exchange messages with web service clients and servers from other programs that rely on SOAP, including Microsoft .NET, Apache Axis, and so on.
  2. A special top-level element within the SOAP body is included to indicate the method that is being called. Its contents are the document that is being transmitted. Most web service tools transmit messages that have a special top-level element used for dispatching.
However, these are just conventions. Part of the power of JWS is that it provides the most common conventions like this by default, yet it also allows you to configure and override these settings if needed. The web service contract we have seen above is simply the most common kind of usage.

For example, if we wanted to configure our web service to accept <order> documents directly over HTTP POST without any SOAP envelopes and without any top-level routing element, it would be a simple matter of adding a few simple JWS annotations to the service.

The code in the following JWS does exactly that:

  import com.scopetrade.orderSchema.*;

  /**
   * @jws:xmlns prefix="t" namespace="http://scopetrade.com/order-schema"
   */
  public class OrderSystem
  {
     /**
      * @jws:operation
      * @jws:protocol http-xml="true" http-soap="false"
      * @jws:parameter-xml schema-element="t:order"
      */
     public String submitOrder(OrderDocument doc)
     {
        return "Got it."
     }
  }

Here is what we have done:

  1. The @jws:protocol tag in the code above disables SOAP-over-HTTP and enables raw non-enveloped XML-over-HTTP,
  2. The @jws:parameter-xml tag indicates that the top-level element in the payload should be <t:order> rather than a routing element. (The namespace prefix "t" is defined at the top.)

    Once you have added those annotations, the message shape and the WSDL contract will change automatically. You will now have a web service where you can submit <order> documents directly to your service by posting them directly over HTTP without any envelopes.
I will leave it to you to try it in Workshop yourself.

The Program

We are now at the last step.

It is worth taking a moment here to notice that, despite all the details we have examined, we have not actually done any programming since step 2 when we put together the XML Schema. We have been just adjusting annotations in the JWS file and examining the automatically generated web service contracts.

Now are ready to actually write a program to use our data, and again, XML Beans and Workshop make this easy to do.

The following is a program that acknowledges the order to the sender, providing the total amount that will be charged for the order, and that prints the order to the console. It is a short program, but because it is driven by an XML Schema, it is highly interoperable and also robust to data errors.

  import com.scopetrade.orderSchema.*;

  /**
   * @jws:xmlns prefix="t" namespace="http://scopetrade.com/order-schema"
   */
  public class OrderSystem
  {
     /**
      * @jws:operation
      * @jws:parameter-xml schema-element="t:order"
      */
     public String submitOrder(OrderDocument doc)
     {
        // Reject documents that are not valid <order>s.

        if (!doc.validate())
           return "The order document was not valid.";

        // Print the order to the console.
        // Obviously in a real app you would enter the order
        // in a database or prepare it to be shown on a web page.

        System.out.println("Received an order:");
        System.out.println(doc.xmlText());

        // Print the top-level note if there is any.

        if (doc.getOrder().isSetNote())
           System.out.println("NOTE: " + doc.getOrder().getNote());

        // Examine the line items to compute the total.

        ProductAndQty[] items = doc.getOrder().getLineItemArray();
        double total = 0.0;
        for (int i = 0; i < items.length; i++)
        {
           double amt = items[i].getPrice() * items[i].getQuantity();
           total = total + amt;
        }

        // Acknowledge the order in the response.

        return "Thanks for your order, " + doc.getOrder().getCustomer()
             + ". Your account will be charged " + total + ".";
     }
  }

I strongly encourage you to try running this program yourself in Workshop. Workshop makes it easy to submit different XML payloads to the service and see what happens.

There are two things to notice in the code above:

  1. Despite the fact that the program is scanning through the XML in a variety of ways, the actual code is extremely short and readable. This ease-of use translates to low development costs, and it is thanks to the strongly-typed Java interface methods such as getLineItemArray() and getQuantity().
  2. Another reason the code is so simple is that it does not need to do error-checking within the main logic of the program. Instead, the data is verified to be free of errors at the very beginning with a call to the validate() method. In the main part of the program, we can depend on the fact that the data that we are processing is clean. This one-shot validation approach is more robust, faster, and easier to maintain than piecemeal error-checking.
The code above is a typical basic usage of XML Beans, and a good example of simple data-oriented programming.

Summary

In this article, we’ve introduced data-oriented programming using XML Beans, and we’ve explored a few of the features of XML Beans.

There are many other data-oriented features in XML Beans that we have not demonstrated in this example. Depending on your specific application, you will be interested in exploring some of these other capabilities. In the documentation in workshop you will find articles and samples that describe how to:

  • Use XML Query and XPath with XML Beans.
  • Use an XML Cursor to manipulate the complete XML token stream.
  • Load and save XML Beans and convert them to and from other XML formats.
  • Build new instances of XML Beans.
  • Use XML Beans outside of a web service.
XML Beans brings all these capabilities together in one place.

Our own experience is that once you have used XML Beans to solve a few problems, your view of programming changes.

You begin to recognize the situations where you are facing a data-oriented problem, typically when you are writing a lot of code dealing with persisted or transmitted data. And once you have used XML Beans, you start to find that it is tedious to try to solve the problem by hand - the way we have always done in the past.

With XML Beans, you find that it becomes much easier to write up a short W3C Schema and use a data-oriented approach. The immediate payoff is that your data-oriented code is clearer, shorter, and more robust. And the long-term payoff is that your program remains highly interoperable and maintainable because of its use of XML standards.

Welcome to the world of XML Beans!

Article Tools

 E-mail
 Print
 Discuss
 Blog

Related Products

Check out the products mentioned in this article: