Saturday, 14 May 2011

An Easy Way to Build a Schema Using XMLBeans inst2xsd

I know that I’ve mentioned this tool before, but I was too brief. This blog rectifies that by demonstrating how easy it is to build your self a schema using a little sample XML and inst2xsd, which is free.

If, like me, you don’t write schemas all that often, then you probably aren’t too fluent in schema definition syntax, but so long as you know some XSD then this method will work for you.

I eventually want to use this schema to specify a web service where you supply a country name as an input parameter and get back a list of its wine details as the result. As usual, this is a contrived example, but it serves my purpose.

There are several simple steps involved in creating a schema that meets your requirements and the first task in specifying a schema is to figure out what namespace you’re using, which in this case is ‘http://marin.tips.spring.webservice/schemas’. Step two is to specify the root element: WineSearch, so far, so good, so simple.

As this will be used for a web service, then the child elements of WineSearch will contain both our input parameter(s) and output results.

The next step is to specify a sample of our input XML:

<Request>
<!-- This is dead simple - just one input parameter -->
     <Country>France</Country>
</Request>

Then, specify a sample of our output XML:

<Response country="France">
     <Wine>
      <Name>Chateau Roger</Name>
      <Origin>Bordeaux</Origin>
      <Strength>11.3</Strength>
      <Colour>Red</Colour>
     </Wine>
     <Wine>
      <Name>Gewurztraminer</Name>
      <Origin>Alsace</Origin>
      <Strength>12.4</Strength>
      <Colour>White</Colour>
     </Wine>
</Response>

Next, combine the above to make a XML sample template:

<?xml version="1.0" encoding="UTF-8"?>
<WineSearch xmlns="http://marin.tips.spring.webservice/schemas">
 <Request>
  <!-- This is dead simple - just one input parameter -->
     <Country>France</Country>
 </Request>
 <!-- Request and response are never together in the same document -->
    <Response country="France">
     <Wine>
      <Name>Chateau Roger</Name>
      <Origin>Bordeaux</Origin>
      <Strength>11.3</Strength>
      <Colour>Red</Colour>
     </Wine>
     <Wine>
      <Name>Gewurztraminer</Name>
      <Origin>Alsace</Origin>
      <Strength>12.4</Strength>
      <Colour>White</Colour>
     </Wine>
    </Response>
</WineSearch>

We can use this with the XMLBeans tool inst2xsd to create a first draft of our schema file. The example command line below assumes that we've saved the XML in a file called: wine-search-sample.xml and will generate a file called schema0.xsd

C:\Java\xmlbeans-2.4.0\bin\inst2xsd wine-search-sample.xml

This is the first-draft of the XSD:

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://marin.tips.spring.webservice/schemas" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="WineSearch" type="sch:WineSearchType" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
  <xs:complexType name="RequestType">
    <xs:sequence>
      <xs:element type="xs:string" name="Country">
        <xs:annotation>
          <xs:documentation>Just one input parameter</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="WineType">
    <xs:sequence>
      <xs:element name="Name">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="Chateau Roger"/>
            <xs:enumeration value="Gewurztraminer"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="Origin">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="Bordeaux"/>
            <xs:enumeration value="Alsace"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="Strength">
        <xs:simpleType>
          <xs:restriction base="xs:float">
            <xs:enumeration value="11.3"/>
            <xs:enumeration value="12.4"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
      <xs:element name="Colour">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="Red"/>
            <xs:enumeration value="White"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="WineSearchType">
    <xs:sequence>
      <xs:element type="sch:RequestType" name="Request" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
      <xs:element type="sch:ResponseType" name="Response" xmlns:sch="http://marin.tips.spring.webservice/schemas">
        <xs:annotation>
          <xs:documentation>Request and response are never together in the same document</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="ResponseType">
    <xs:sequence>
      <xs:element type="sch:WineType" name="Wine" maxOccurs="unbounded" minOccurs="0" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="country"/>
  </xs:complexType>
</xs:schema>

Given that our initial XML sample is just that, a sample, then we’ll need to make some alterations to the above schema, and the first step here is to modify the restrictions imposed by inst2xsd.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://marin.tips.spring.webservice/schemas" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="WineSearch" type="sch:WineSearchType" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
  <xs:complexType name="RequestType">
    <xs:sequence>
      <xs:element type="xs:string" name="Country">
        <xs:annotation>
          <xs:documentation>Just one input parameter</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="WineType">
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Origin" type="xs:string" / >
      <xs:element name="Strength" type="xs:float">
      <xs:element name="Colour">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="Red"/>
            <xs:enumeration value="White"/>
            <xs:enumeration value="Rose"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="WineSearchType">
    <xs:choice>
      <xs:element type="sch:RequestType" name="Request" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
      <xs:element type="sch:ResponseType" name="Response" xmlns:sch="http://marin.tips.spring.webservice/schemas">
        <xs:annotation>
          <xs:documentation>Request and response are never together in the same document</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:choice>
  </xs:complexType>
  <xs:complexType name="ResponseType">
    <xs:sequence>
      <xs:element type="sch:WineType" name="Wine" maxOccurs="unbounded" minOccurs="0" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="country"/>
  </xs:complexType>
</xs:schema>

Notice that the above also includes the tightening of one restriction in the addition of an extra wine colour: Rose.

Finally, I said above that this schema will be used for a web service, which means that we don’t want the Request and Response elements in the same document.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://marin.tips.spring.webservice/schemas" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="WineSearch" type="sch:WineSearchType" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
  <xs:complexType name="RequestType">
    <xs:sequence>
      <xs:element type="xs:string" name="Country">
        <xs:annotation>
          <xs:documentation>Just one input parameter</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="WineType">
    <xs:sequence>
      <xs:element name="Name" type="xs:string" />
      <xs:element name="Origin" type="xs:string" / >
      <xs:element name="Strength" type="xs:float">
      <xs:element name="Colour">
        <xs:simpleType>
          <xs:restriction base="xs:string">
            <xs:enumeration value="Red"/>
            <xs:enumeration value="White"/>
            <xs:enumeration value="Rose"/>
          </xs:restriction>
        </xs:simpleType>
      </xs:element>
    </xs:sequence>
  </xs:complexType>
  <xs:complexType name="WineSearchType">
    <xs:choice>
      <xs:element type="sch:RequestType" name="Request" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
      <xs:element type="sch:ResponseType" name="Response" xmlns:sch="http://marin.tips.spring.webservice/schemas">
        <xs:annotation>
          <xs:documentation>Request and response are never together in the same document</xs:documentation>
        </xs:annotation>
      </xs:element>
    </xs:choice>
  </xs:complexType>
  <xs:complexType name="ResponseType">
    <xs:sequence>
      <xs:element type="sch:WineType" name="Wine" maxOccurs="unbounded" minOccurs="0" xmlns:sch="http://marin.tips.spring.webservice/schemas"/>
    </xs:sequence>
    <xs:attribute type="xs:string" name="country"/>
  </xs:complexType>
</xs:schema>

Et voila, the schema is ready to use.

1 comment:

Amir said...

Thanks for taking the time to make this post. I've used this method over and over again.