Java Types Generated from User-Derived Schema Types

When you compile XML schema, the resulting API is made up of two categories of types: built-in types that mirror those in the schema specification and others that are generated from user-derived schema types. This topic provides an overview of the Java types generated for user-derived types, describing the methods the Java types provide. For more information about built-in types, see XMLBeans Support for Built-In Schema Types. For specific information about the methods exposed by generated types, see Methods for Generated Java Types.

In general, an API generated from schema is an intuitive means to access XML instances based on the schema. You'll probably find that for most uses it's unnecessary to know the rules for generating it in order to use it. However, for those cases when it's unclear what's going on behind the scenes (or if you're just curious), this topic describes the rules.

Note: The XMLBeans API also provides a way for you to get information about the type system itself — in other words, about the API and the underlying schema. For more information, see Introduction to Schema Type Signatures.

Each of the types generated when you compile a schema is designed specifically for access to XML instances conforming to that part of the schema. Start by taking a look at a simple XML and schema example. The following schema describes an XML document to contain a stock price quote.

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <xs:element name="price-quote">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="stock-symbol" type="xs:string"/>
                <xs:element name="stock-price" type="xs:float"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

The following is an example of XML that conforms to this schema.

<price-quote>
    <stock-symbol>BEAS</stock-symbol>
    <stock-price>59.21</stock-price>
</price-quote>

When you compile this schema, you get two generated XMLBeans interfaces: PriceQuoteDocument and PriceQuoteDocument.PriceQuote.

From the schema point of view, the generated PriceQuote interface represents the complex type you see inside the schema's price-quote element declaration. Looking at the XML instance, you can see that this complex type translates into a sequence of two elements, stock-symbol and stock-price. So it's not surprising that the PriceQuote interface exposes methods such as getStockPrice and setStockPrice to set the value stock-price element.

The PriceQuoteDocument interface, on the other hand, represents the price-quote document that contains the root price-quote element. XMLBeans creates a special "document" type for global element types. A document type provides a way for you to get and set the value of the underlying type, here represented by PriceQuote. The price-quote element is considered a global element because it can be referenced from anywhere else in the schema. For global elements, the XMLBeans schema compiler generates an interface whose name ends with "Document." This is because an XML schema has no way of defining a "root" element; any global element can be the root.

The following bit of Java code illustrates how you might use these interfaces to get the stock price contained in the XML.

public static float getStockPrice(java.io.File orderXML) throws Exception
{
    PriceQuoteDocument docXML = PriceQuoteDocument.Factory.parse(orderXML);
    PriceQuote quoteXML = docXML.getPriceQuote();
    float stockPrice = quoteXML.getStockPrice();
    return stockPrice;
}

This code loads the XML from a File object, converting the parse method's return value to a PriceQuoteDocument instance. It then uses this instance to get an instance of PriceQuote. With PriceQuote, the code extracts the stock price.

The XML schema specification provides a rich set of rules through which you can derive new types. When you generate interfaces from your schema, XMLBeans uses the schema's rules to determine how to generate interfaces. The following describes some of the guidelines by which this is done.

Names for Interfaces

Interfaces are generated for schema types (both simple and complex). Anonymous schema types result in inner interfaces inside the type interface in which they are defined. Their name comes from the element or attribute in which they is defined.

Names for schema types become Java-friendly names when the schema is compiled. In other words, names such as "price-quote" become "PriceQuote." In addition, a schema's XML namespace URIs become package names for XMLBean types generated from the schema. The way this name translation is done is described by section C of the Java API for XML Binding (JAXB) specification at http://java.sun.com/xml/jaxb.html.

Here are a few examples:

Schema Target Namespace XML Localname Fully-Qualified XMLBean Type Name
http://www.mycompany.com/2002/buyer purchase-order-4 com.mycompany.x2002.buyer.PurchaseOrder4
http://myco.com/sample.html SampleDocument com.myco.sample.SampleDocument
http://openuri.org/test_case_1 test_type org.openuri.testCase1.TestType

When there are name collisions, the generated types will have names with numerals appended  for example, "TestType2".

Global Elements and Attributes

In schema, global element and attribute definitions are those that are declared at the top level of the schema (that is, immediately within the schema root element). Because they are global, they may be referenced from inside the schema by name. The creditReport (not the creditReportType complex type) element defined in the following schema is an example.

<xs:schema targetNamespace="http://openuri.org/samples/creditReport"
    xmlns:cr="http://openuri.org/samples/creditReport"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">
    <xs:complexType name="creditReportType">
        <xs:sequence>
            <xs:element name="bankReport" type="xs:string"/>
            <xs:element name="taxReport" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:element name="creditReport" type="cr:creditReportType"/>
</xs:schema>

XMLBeans generates a separate interface for each of these. Also, global element and attribute types are somewhat unique in that the schema compiler will create special types to hold the globally defined element or attribute. The names of these types will be appended with "Document" (for elements) or "Attribute" (for attributes). You can retrieve the element or attribute itself (or create a new one) by calling the accessor methods that the special types provide. The following example would create a new instance of the creditReport element.

// Create an instance of the special document type.
CreditReportDocument reportDoc = CreditReportDocument.Factory.newInstance();
/*
 * Use the document type to add a new creditReport element to the XML instance.
 * Note that the type returned by the addNewCreditReport method is the
 * creditReportType complex type defined for it in schema.
 */
CreditReportType report = reportDoc.addNewCreditReport();

Global User-Derived Types

A user-derived type is one defined with a complexType or simpleType element in schema. User-derived types at the top level of a schema are global. XMLBeans generates an interface for each of these, as it does with global elements and attributes. These interfaces include methods through which you can get and set the type's values, including any nested derived types it may contain. The following schema snippet defines a user-derived complex type called itemType, along with a priceType that uses it as the type for an item child element.

<xs:complexType name="itemType">
    <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="amount" type="xs:int"/>
        <xs:element name="price" type="xs:double"/>
    </xs:sequence>
</xs:complexType>
<xs:complexType name="priceType">
    <xs:sequence>
        <xs:element name="item" type="ps:itemType" minOccurs="0" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>
<xs:element name="price" type="ps:priceType"/>

By default, the generated Java type for itemType would be an interface called ItemType. With this type, you would be able to get and set the values of its name, amount, and price child elements. However, a user-derived type (as opposed to an element or attribute) is always intended for use as the type to which an element or attribute is bound. In other words, it's contained by an element or attribute. While you can create a new instance of a user-derived type, the new instance's underlying XML is a fragment. As the generated API would make clear, the itemType becomes the return type of a get method, or the parameter of a set method.

// Create a new price document.
PriceDocument priceDoc = PriceDocument.Factory.newInstance();
PriceType price = priceDoc.getPrice();

/*
 * Create a new instance of ItemType and set the values of its
 * child elements.
 */
ItemType item = ItemType.Factory.newInstance();
item.setName("bicycle");
item.setAmount(12);
item.setPrice(560.00);

/*
 * Use the new ItemType instance to set the value of the
 * price element's first item child element. Notice that the set method
 * here is an "Array" method. This is because the item element
 * is defined with a maxOccurs="unbounded" attribute. It can occur
 * many times as a child of price.
 */
price.setItemArray(0, item);

Nested Elements and Derived Types

When your schema includes named types that are declared locally within the declaration of another element or type the schema type's generated Java interface will be an inner interface within the type it's nested in.

For example, the following schema snippet defines name and gender elements nested within a person complex type. In particular, note that the gender element is defined as deriving from the xs:NMTOKEN built-in type.

<xs:complexType name="person">
    <xs:sequence>
        <xs:element name="name" type="xs:string"/>
        <xs:element name="gender">
            <xs:simpleType>
                <xs:restriction base="xs:NMTOKEN">
                    <xs:enumeration value="male"/>
                    <xs:enumeration value="female"/>
                </xs:restriction>
            </xs:simpleType>
        </xs:element>
    </xs:sequence>
</xs:complexType>

The generated interfaces for person and gender would be organized in source something like the following. Of course, you wouldn't see the source, but you can see here that the Gender interface is nested with Person. Also, notice that it extends XmlNMTOKEN, mirroring the schema.

public interface Person extends XmlObject
{
    public interface Gender extends XmlNMTOKEN
    {
        // Methods omitted for this example
    }
    // Methods omitted for this example
}

You could create a new instance of the Gender type in this way (there are also various alternatives to this):

// Create a new Person instance.
Person person = Person.Factory.newInstance();
/*
 * Set the gender element's value using the
 * enumeration generated from the schema.
 */
person.setGender(Gender.FEMALE);

User-Derived Simple Types

In addition to the 46 built-in simple types in XML schema, a schema can include its own custom simple types using xs:simpleType declarations. These user-derived simple types are always based on the built-in XML schema types. The built-in types can be modified by restricting them, taking unions of them, or making space-separated lists of them. Each XML simple type is translated into a Java type that provides access to the underlying data.

Unions

In schema, you can use xs:union to specify a simple type that is allowed to contain values of a number of other simple types. XMLBeans generates a type for a union, just as it generates a type for any other schema type. At run time, you can discover the underlying type of an instance of a union type by calling the XmlObject interface's instanceType method. Once you have determined the type, you can cast an instance of a union type to the actual underlying instance type.

<xs:simpleType name="intOrString">
    <xs:union memberTypes="xs:int xs:string">
</xs:simpleType>

Given the preceding schema snippet, you could set the intOrString value to, say, 6 or "six". The union of xs:int and xs:string makes both allowable.

// Create a new instance of the type.
IntOrString intOrString = IntOrString.Factory.newInstance();
intOrString.set("5");
// This code prints "XmlInt" to the console.
System.out.println(intOrString.instanceType().getShortJavaName());

Restrictions

XML schema restrictions on simple XMLBeans types are enforced. So, for example, it is illegal to set a number outside its restricted range.

Numeric Type Restrictions

In schema, you can restrict numeric types to allow, for example, only a particular range of values. For such a restriction, XMLBeans tailors the resulting natural Java alternative. For example, suppose you have the following element defined in schema:

<xs:element name="number">
    <xs:simpleType>
        <xs:restriction base="xs:integer">
            <xs:minInclusive value="1"/>
            <xs:maxInclusive value="1000000"/>
        </xs:restriction>
    </xs:simpleType>
</xs:element>

The type is restricted from xs:integer, but because the number's range is limited to between 1 and 1000000, it will fit into a Java int. A long or java.math.BigInteger would be too big for the need. In other words, the getNumber method generated for this type will return an int, rather than a BigInteger or a long.

By the same token, an long can be compiled to an int if the totalDigits attribute is <=9, or the min and max attribute values are within 32-bit 2s complement range.

The single primitive XML type xs:decimal can be restricted in several ways that influence the resulting natural Java type. For example, it can be:

Enumerations

In schema, you can derive a new type by restricting a built-in type so that only a finite set of values are allowable. Where schema does this by restricting xs:string, XMLBeans generates a special Enum type. With an Enum, you can select the enumerated value either by its String value or by a numeric index. The index's value is determined based on the String value's order in the schema. Having an index can be useful in Java switch statements.

For example, suppose you had a document containing price elements whose type was the priceType defined in the following schema snippet:

<xs:complexType name="priceType">
    <xs:sequence>
        <xs:element name="item" type="ps:itemType" minOccurs="0"
              maxOccurs="unbounded"/>
    </xs:sequence>
    <xs:attribute name="threshold">
        <xs:simpleType>
            <xs:restriction base="xs:string">
                <xs:enumeration value="Below10Dollars"/>
                <xs:enumeration value="Between10And20Dollars"/>
                <xs:enumeration value="Above20Dollars"/>
            </xs:restriction>
        </xs:simpleType>
    </xs:attribute>
</xs:complexType>

Using types generated from the schema, you would be able to write the following Java code to "switch" on the threshold attribute's enumeration:

/*
 * Use the intValue method provided by the Enum type to determine the threshold
 * attribute's current enumeration value.
 */
switch(priceElements[i].getThreshold().intValue())
{
    // Use the Threshold type's enumeration values to test for an attribute value.
    case PriceType.Threshold.INT_BELOW_10_DOLLARS:
        zeroBuffer.append(item.getTitle() + "\n");
        break;
    case PriceType.Threshold.INT_BETWEEN_10_AND_20_DOLLARS:
        tenBuffer.append(item.getTitle() + "\n");
        break;
    case PriceType.Threshold.INT_ABOVE_20_DOLLARS:
        twentyBuffer.append(item.getTitle() + "\n");
        break;
    default:
        System.out.println("Yo! Something unexpected happened!");
        break;
}

Related Topics

XMLBeans Support for Built-In Schema Types