Java and Spring development

Generate JAXB classes from an XSD schema and the schema from an XML sample document

with 10 comments

Introduction

This tutorial demonstrates how to generate an XSD schema from a sample XML document and how to improve the schema before generating JABX classes from it.

The sample document used throughout this tutorial contains an order element as you can see here:

<order>
	<date>2010-01-01</date>
	<id>12345</id>
	<item name="Effective Java" type="BOOK" quantity="1" />
	<item name="Spring in Action" type="BOOK" quantity="1" />
	<item name="Gladiator" type="DVD" quantity="1" />
</order>

The motivation for creating a sample document first, is that it’s more simple and less verbose to write than an XSD schema. You can also generate an XSD schema from JAXB classes, but this tutorial generates JAXB classes from an XSD schema. This is because an XSD schema is language neutral and more feature rich for describing a data contract. For example the ability to add restrictions with regular expressions like this:

<xs:attribute name="number" use="required">
	simpleType>
		<xs:restriction base="xs:string">
			<xs:pattern value="\d{10}" />
		</xs:restriction>
	</xs:simpleType>
</xs:attribute>

Generate XSD schema from XML sample document with Trang

The Trang project lives here: http://code.google.com/p/jing-trang/

You can execute Trang’s Driver class with the XML sample document’s path and the path for the schema that will be generated to generate the schema. Another and probably more common way to generate is with Ant like this target:

<target name="generate-order-schema" description="Generates schema from XML with Trang">
	<mkdir dir="${xsd-folder}" />

	<java jar="${lib-generate-folder}/trang.jar" fork="true">
		<arg value="${xml-example-folder}/order.xml" />
		<arg value="${xsd-folder}/order-generated.xsd" />
	</java>

	<echo message="${xsd-folder}/order-generated.xsd was generated" />
</target>

After you have executed this target, remember to update the folder with the generated file if you’re using Eclipse.

Tweaking the generated XSD schema

I only specified the input file and the path to the generated file when I executed Trang. This results in a very simple XSD without namespace for example. See Trang’s manual for more advanced use. The generated schema looks like this:

<?xml version="1.0" encoding="UTF-8"?>
xmlns:xs="http://www.w3.org/2001/XMLSchema"
	elementFormDefault="qualified">
	<xs:element name="order">
		<xs:complexType>
			<xs:sequence>
				<xs:element ref="date" />
				<xs:element ref="id" />
				<xs:element maxOccurs="unbounded" ref="item" />
			</xs:sequence>
		</xs:complexType>
	</xs:element>
	NMTOKEN" />
	<xs:element name="id" type="xs:integer" />
	<xs:element name="item">
		<xs:complexType>
			<xs:attribute name="name" use="required" />
			<xs:attribute name="quantity" use="required" type="xs:integer" />
			NCName" />
		</xs:complexType>
	</xs:element>
</xs:schema>

The generated schema is an excellent start, but you often want to add namespace, restrictions and often to change the type of some of the fields Trang guessed on. After some manual modifications, my schema looks like this:

<?xml version="1.0" encoding="UTF-8"?>
xmlns:xs="http://www.w3.org/2001/XMLSchema"
	elementFormDefault="qualified" targetNamespace="http://www.redpill-linpro.com/order"
	xmlns:o="http://www.redpill-linpro.com/order">

	<xs:element name="order">
		<xs:complexType>
			<xs:sequence>
				<xs:element name="date" type="xs:date" />
				<xs:element name="id" type="xs:int" />
				<xs:element name="item" maxOccurs="unbounded">
					<xs:complexType>
						<xs:attribute name="name" use="required" type="xs:string" />
						<xs:attribute name="quantity" use="required" type="xs:int" />
						itemType" />
					</xs:complexType>
				</xs:element>
			</xs:sequence>
		</xs:complexType>
	</xs:element>

	<xs:simpleType name="itemType">
		<xs:restriction base="xs:string">
			<xs:enumeration value="BOOK" />
			<xs:enumeration value="DVD" />
			<xs:enumeration value="CD" />
		</xs:restriction>
	</xs:simpleType>
</xs:schema>

Another difference is that it’s only one xs:element in the tweaked version. This is because id or date doesn’t give any value alone, but only together as part of the order element.

A JAXB tips is to create an xs:element with a nested type element inside it. This enables JAXB to add an @XmlRoot annotation above the generated class. This annotation is necessary for JAXB to marshall your objects.

Working with XSD schema inside a good IDE

With an IDE like Eclipse, it’s quite easy to create an XML document from an XSD schema. The XML document below is created with Eclipse. The schema is relative to the document and is highlighted in the XML document. You should consider a URL instead of a relative path for other situations than simple testing.

<?xml version="1.0" encoding="UTF-8"?>
xmlns:o="http://www.redpill-linpro.com/order"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://www.redpill-linpro.com/order order.xsd ">
	<o:date>2001-01-01</o:date>
	<o:id>0</o:id>
	<o:item name="Effective Java" quantity="3" type="BOOK" />
	<o:item name="Spring in Action" quantity="1" type="BOOK" />
</o:order>

A good IDE that’s aware of the schema will also give you code completion and validation support. This screenshot shows code completion of enumeration values.

Testing the generated JAXB classes

The test below is a simple test that instantiates a JAXB generated order object. Then the object is marshalled to an XML document. The test asserts that the XML document contains some of the data from the order object. After this, it unmarshall the XML document back to an Order object again. Finally, the two order objects are compared against each other.

public class OrderJaxbTest {

	@Test
	public void testOrder() throws Exception {
		Order order = createJaxbOrder();

		String xmlDocument = marshallOrderToXml(order);
		assertTrue(xmlDocument.contains("type=\"CD\" quantity=\"2\" name=\"Effective Java\""));

		Order unmarshalledOrder = unmarshallToOrderFromXml(xmlDocument);
		assertEquals(order.getId(), unmarshalledOrder.getId());
	}

	private Order unmarshallToOrderFromXml(String xmlDocument)
			throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(Order.class);
		Unmarshaller unmarshaller = context.createUnmarshaller();
		StringReader reader = new StringReader(xmlDocument);
		Order unmarshalledOrder = (Order) unmarshaller.unmarshal(reader);
		return unmarshalledOrder;
	}

	private Order createJaxbOrder() throws DatatypeConfigurationException {
		Order order = new ObjectFactory().createOrder();
		order.setDate(DatatypeFactory.newInstance()
				.newXMLGregorianCalendar(2010, 5, 5, 12, 0, 0, 0, 1));
		order.setId(1);

		Item item = new Item();
		item.setName("Effective Java");
		item.setQuantity(2);
		item.setType(ItemType.CD);
		order.getItem().add(item);

		return order;
	}

	public String marshallOrderToXml(Order order) throws JAXBException {
		JAXBContext context = JAXBContext.newInstance(Order.class);
		Marshaller marshaller =context.createMarshaller();
		StringWriter writer = new StringWriter();
		marshaller.marshal(order, writer);

		return writer.toString();
	}
}

For simplicity, I send an StringWriter to the JAXB marshaller and an StringReader to the JAXB unmarshaller. It’s just to replace the reader and writer if you want to work against the file system.

Summary

To generate a basic version of the schema and then incrementally improve that schema works really well. It exists several frameworks and tools that can generate the schema. I have only evaluated Trang, but it does the job well and it’s an open source project that’s very easy to use. Also, to generate the JAXB classes from an XSD schema ensures that you’re 100% language neutral. JAXB is just one of many frameworks that can parse XML. It’s the Java standard parsing framework and some of its benefits are that it provides type safety and abstracts away the XML parsing behind a Java API.

To generate JAXB classes from an XSD schema is also an excellent start for writing contract-first web services with frameworks like Spring WS and CXF. These frameworks can be configured to use JAXB for the parsing. I will write more about Spring WS in a later article.

The code used in this article was developed in my work time at Redpill Linpro. It can be downloaded from here.


Redpill Linpro is the leading provider of Professional Open Source services and products in the Nordic region.

Written by Espen

February 26, 2010 at 12:36

Posted in Web Services

Tagged with , , ,

10 Responses

Subscribe to comments with RSS.

  1. I guess the part I’m missing must be too trivial to cover – the actual generation of the JAXB classes. Where is that discussed? Sorry, new to this.

    B. Barrington

    July 9, 2010 at 01:04

  2. I agree with B. Barrington. Where is the explanation of generating the Order class? From the title of this blog I thought that would have been the whole point of the post.

    jtsnr

    November 27, 2011 at 18:24

  3. To generate the classes in Eclipse, right-click on the xsd file and select Generate->JAXB Classes…

    jtsnr

    November 27, 2011 at 18:32

    • You write: “A JAXB tips is to create an xs:element with a nested type element inside it. This enables JAXB to add an @XmlRoot annotation above the generated class. This annotation is necessary for JAXB to marshall your objects.”

      JAXB is a specification, not a product. eclipselink, for example, is a product implementing JAXB (along with other things). Using an unmodified Trang xsd file as input, eclipselink adds an @XmlRoot annotation for each class it generates. There is no *need* to modify the xsd file

      The only effect of modifying a Trang xsd file in the way you did, is that eclipselink generates a single top class, the other classes being generated as a nested hierarchy of inner classes of the top one,. This is not user-friendly code.

      Stuart Clayton

      May 6, 2012 at 10:27

  4. hello every body … i am really new in this, i am tryning to use trang API, before all of that, please can any body tell me how to exute it ? this my email : lamara.aymen@hotmail.fr pls help me !!!

    note if some body send me an email pls send it with a title api trang so i can know. thks

    L.aymen

    September 24, 2013 at 21:58

  5. i found the correct form t excute the trang jar file from command window on windows “java -jar trang.jar -I xml -O xsd fileinput.xml … fileoutput.xsd” but it alwayssays o me file not found … exists, but not accessible … i doun’t understand why is that ??

    L.aymen

    September 25, 2013 at 17:59

  6. It’s hard to find your page in google. I found it on 20
    spot, you should build quality backlinks , it will
    help you to get more visitors. I know how to help you, just
    search in google – k2 seo tips

    Rudolf

    July 5, 2014 at 01:30

    • thank you i v found it ^^.

      Anonymous

      November 3, 2014 at 18:07

  7. […] Generate JAXB classes from an XSD schema and the schema … – Generate JAXB classes from an XSD schema and the schema from an XML sample document. with 8 comments […]

  8. […] Generate JAXB classes from an XSD schema and the … – Generate JAXB classes from an XSD schema and the schema from an XML sample document. with 8 comments […]


Leave a comment