ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

The State of JAXB: Availability, Suitability, Analysis, and Architecture
Pages: 1, 2, 3, 4

JAXB Allows Customization of Class Generation

The generated Java classes are customizable using either inline annotation or external annotation files. These external files are XML files that identify a node of the XSD and provide additional configuration for that node/element. In the .NET world, a similar goal is accomplished through .NET's attribute technology. Those familiar with this .NET technology should be able to draw parallels between the two approaches.



Example of an Inline Annotation

The subject of customizing JAXB bindings is covered in much more depth by Sayed Hashimi in his ONJava article, "Understanding JAXB: Java Binding Customization." Architecturally, it will be illustrative if I can show an inline annotation to add an isSet method to the XSD that we have.

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
            xmlns:jxb="http://Java.sun.com/xml/ns/JAXB"
            jxb:version="1.0"
            elementFormDefault="qualified" 
            attributeFormDefault="unqualified">
<!--
**************************************************
* Notice the XSD annotation elements
**************************************************
-->
<xs:complexType name="file">
    <xs:all>
        <xs:element name="description"
            type="xs:string">
            <xs:annotation>
               <xs:appinfo>
                  <jxb:property
                    generateIsSetMethod="true"/>
               </xs:appinfo>
            </xs:annotation>
        </xs:element>            
        <xs:element name="contents" type="xs:string"/>
    </xs:all>
    <xs:attribute name="id" type="xs:string"/>
    <xs:attribute name="name" type="xs:string"/>
</xs:complexType>

<!--
**************************************************
* folder definition
**************************************************
-->
<xs:element name="folder">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="file" type="file" 
	         minOccurs="0" maxOccurs="unbounded"/>
        </xs:sequence>        
        <xs:attribute name="id" type="xs:string"/>
        <xs:attribute name="name" type="xs:string"/>
    </xs:complexType>
</xs:element>
</xs:schema>

The inline bold section will instruct the JAXB binding generator to generate a check method for setting the description property of the file class. This scenario is similar to the null values in databases: a column in a database can be checked not only for its value but to see if it has been set at all.

Example of an External Annotation File

JAXB also allows extensive customization using external files as well, to cover the cases where it is not possible or convenient to modify the XSD document. Here is an example of an external file that does the same thing as the above XSD annotation.

<jxb:bindings version="1.0"
               xmlns:jxb="http://Java.sun.com/xml/ns/JAXB"
               xmlns:xs="http://www.w3.org/2001/XMLSchema">
...some stuff
    <jxb:bindings node="//xs:complexType[@name='file']">
          <jxb:bindings node=".//xs:element[@name='description']">
                  <jxb:property generateIsSetMethod="true"/>
          </jxb:bindings>
    </jxb:bindings>
...some more stuff
</jxb:bindings>

I have included just the relevant section for the description node. For the actual file syntax look in the JAXB\samples\ subdirectory in the JAXB distribution.

One can also alter the original XSD to suit the needs of customization. This aspect is also covered in some detail in the above-mentioned article. These issues are architecturally significant, as one can get a sense of how JAXB works from a high-level perspective.

What Can Be Customized?

Now we know that JAXB can be customized, let us examine what aspects can be customized. Package names can be customized. Derivation can be controlled, to some extent. Method generation can be customized. The other customizable aspects include:

  • Choose which elements to bind to classes.
  • Decide how to bind each attribute and element declaration to a property in the appropriate content class.
  • Choose the type of each attribute-value or content specification.

Like many other APIs in J2EE, JAXB is designed so that there can be more than one implementation that adheres to the same defined interface. JAXB also provides a range of validation services whereby the structure of XML and Java objects can be directly checked against a schema document. JAXB also seems to have been well thought out, offering multiple input and output mechanisms that include input/output streams, URLs, DOM, SAX, and javax.xml.transform.SAX.SAXSource. Whether you like it or not, JAXB uses a factory-based approach to create Java objects that are capable of working with XML. JAXB also allows the manipulation of the marshaller to tweak the XML output.

Programming Example Using JAXB

Let me show you a client program using the JAXB-generated Java classes.

//************************************************
//* Package and includes
//************************************************
package com.ai.xmltest;
import Java.util.*;
import Javax.xml.bind.*;

//************************************************
//* main class
//************************************************
public class XMLTest 
{
//************************************************
//* Get a JAXBContext one time
//************************************************
    private static JAXBContext jc = null;
    public static void main(String[] args) 
    {
        try
        {
            System.out.println("Establish the context");
            jc = JAXBContext.newInstance("com.ai.xmltest");
            
            System.out.println("Create a Java object" +
                "and stream it out as xml");
            JavaToXML();
            
            System.out.println("Read an xml file as a Java " +
                "object and stream it back as xml");
            xmlToJava();
            
        }
        catch (JAXBException x)
        {
            x.printStackTrace();
        }
    }//eof-main

//************************************************
//* Java to XML
//************************************************
    public static void JavaToXML() 
                        throws JAXBException
    {
        ObjectFactory of = new ObjectFactory();
        File file = of.createFile();
        file.setId("1");
        file.setName("file1");
        file.setDescription("description");
        file.setContents("longtext");
            
        Folder folder = of.createFolder();
        folder.setId("1");
        folder.setName("folder1");
        List fileList = folder.getFile();
        fileList.add(file);
        
        printFolder(folder);
    }
//************************************************
//* XML to Java
//************************************************
    public static void xmlToJava()
                    throws JAXBException
    {
        Java.io.File xmlfile = new Java.io.File(
            "C:\\satya\\data\\eclipse\\JAXB\\sample.xml");
        
        Unmarshaller um = jc.createUnmarshaller();
        Folder folder = (Folder)um.unmarshal(xmlfile);
        
        printFolder(folder);
    
    }
    
//************************************************
//* Print support
//************************************************
    public static void printFolder(Folder folder)
                    throws JAXBException
    {
        Marshaller m = jc.createMarshaller();
        m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                      new Boolean(true));
        m.marshal(folder,System.out);
    }
}//eof-class

A JAXB client typically works with three objects: context, marshaller, and unmarshaller. The process is quite straightforward and easy. Nevertheless, obtaining the JAXBContext is quite fishy. Actually, getting provider classes for a given API throughout the J2EE API is quite inconsistent. In this case, you have to pass the package names to the static function of newInstance to locate the provider for the JAXBContext. Passing package names for locating implementation objects seem odd.

Also notice how we have to use the ObjectFactory to obtain the instances of the Java classes. In addition, we have to use a series of set methods to populate these objects. A constructor would have been better for mandatory arguments. But JAXB being interface-centric, this is not really possible. Let us take a look at how .NET does the same thing.

Programming Example Using .NET

//************************************************
//* packages and imports
//************************************************
using System;
using System.Data;
using System.Collections;
using System.Xml.Serialization;
using System.IO;
namespace XMLSerializationTest
{
    class DBTest
    {
        [STAThread]
        static void Main(string[] args)
        {
            //Read the xml from a file into an object
            //Serialize the xml back to output stream
            folder f = xmlToCode();
            System.Console.WriteLine(
                "XML to code to print stream");
            printObjectAsXML(f);

            //code to xml
            f = codeToXML();
            System.Console.WriteLine(
                "Code to XML to print stream");
            printObjectAsXML(f);

        }//eof-main
//************************************************
//* xml to code
//************************************************
        static folder xmlToCode()
        {
            FileStream fs = null;
            try
            {
                fs = new System.IO.FileStream("sample.xml",
                    System.IO.FileMode.Open);
                //returns the folder object    
                XmlSerializer s =
                    new XmlSerializer(typeof(folder));
                folder o = (folder)s.Deserialize(fs);
                return o;
            }
            finally
            {
                if (fs != null) 
                {
                    fs.Close();
                }
            }
        }
//************************************************
//* code to xml
//************************************************
        static folder codeToXML()
        {
            //Create a folder
            //id is 1, name is folder1,
            // and holds two items
            folder folder = new folder("1","folder1",2);

            //dotnet uses a typed array to bind to xml
            //children. This is a nuisance as you need
            //to know the length of the array to
            //populate it before hand. In addition it
            //is the programmer's responsibility
            //to initialize the array
            file file1 = new file("1","file1");
            file1.contents="file1 conent";

            file file2 = new file("1","file2");
            file2.contents="file2 contents";

            folder.files[0]=file1;
            folder.files[1] = file2;
            return folder;
        }
//************************************************
//* print support
//************************************************
        static void printObjectAsXML(Object obj)
        {
            XmlSerializer x =
                new XmlSerializer(obj.GetType());
            x.Serialize(System.Console.Out,obj);
        }
    }//eof-class
}//eof-namespace

One main difference is that you can instantiate .NET objects directly without any factories. You can add constructors for mandatory arguments. One odd thing with .NET is that it uses arrays. This requires a workaround for populating child objects. JAXB has a better approach, where they use dynamic list objects. But otherwise, the .NET's approach is much more coder-friendly and fairly separated from the XSD requirement, making code more central. In JAXB, XSD is more central to the process.

Pages: 1, 2, 3, 4

Next Pagearrow