Rebecca Searls' Blog
Jaxb: Adding Behaviors
You can add behaviors to jaxb generated classes by subclassing and overriding methods. This technique is useful when integrating the generated classes into an existing codebase, and when data alteration is needed post unmarshalling or during marshalling. Currently there is no mechanism to alter data during the unmarshalling process. The unmarshalling process entails writing the data into the jaxb generated class object (i.e. superclass) of any subclass you provide, however each subclass will be able to access the data from its superclass post unmarshalling.
There are several implementation rules that must be followed when adding behaviors to jaxb generated classes.
- You must create a subclass of the jaxb generated ObjectFactory class and it must be tagged with the @XmlRegistry annotation.
- You must override any of the methods which are to provide a new subclass.
- Your subclasses may use the same package name as the jaxb generated classes however it is recommend to put them in a separate package and it not be the empty package.
- Your new ObjectFactory class will be identified to the JAXContext by setting property "com.sun.xml.bind.ObjectFactory" to an instance of this class.
mkdir b
xjc -d b -p gbooks.inv books.xsd
books.xsd
<?xml version="1.0"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jxb="http://java.sun.com/xml/ns/jaxb"
jxb:version="2.0">
<xsd:element name="BookInventory">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="Book" type="bookType"
minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:complexType name="bookType">
<xsd:sequence>
<xsd:element name="isbin" type="xsd:string" />
<xsd:element name="title" type="xsd:string" />
<xsd:element name="price" type="xsd:double" />
</xsd:sequence>
</xsd:complexType>
</xsd:schema>
Three classes are generated from this schema, ObjectFactory, BookInventory, and BookType.
gbooks/inv/ObjectFactory.java
@XmlRegistry
public class ObjectFactory {
public BookInventory createBookInventory() { return new BookInventory(); }
public BookType createBookType() { return new BookType(); }
}
gbooks/inv/BookInventory.java
public class BookInventory {
public List<BookType> getBook() {
if (book == null) { book = new ArrayList<BookType>(); }
return this.book;
}
}
gbooks/inv/BookType.java
public class BookType {
public String getIsbin() { return isbin; }
public void setIsbin(String value) { this.isbin = value; }
public String getTitle() { return title; }
public void setTitle(String value) { this.title = value; }
public float getPrice() { return price; }
public void setPrice(float value) { this.price = value; }
}
This example adds behaviors to BookType. Class RM_BookType is extended from the jaxb generated class gbooks.inv.BookType and the methods of interest are overridden. Text is prepended to the isbin string and written back into the superclass, BookType. Any future references to this value in object BookType will be the new value. 10% is added to the original price. This change occurs only when class object RM_BookType is referenced.
RM_BookType
package read.more;
public class RM_BookType extends gbooks.inv.BookType {
@Override
public void setIsbin(String value) {
//- check for initial instance of this class;
//- parent would not have a value for isbin.
if (super.getIsbin() == null)
super.setIsbin(value);
else
super.setIsbin(value + super.getIsbin());
}
@Override
public double getPrice() {
// add a 10% handling fee to book
return super.getPrice() * 0.1 ;
}
}
Next we must subclass from the jaxb generated ObjectFactory. RM_ObjectFactory extends gbooks.inv.ObjectFactory. It is annotated with @XmlRegistry as is required. It has a package name of read.more. Method createBookType is overridden in order to provide an instance of RM_BookType.
RM_ObjectFactory
package read.more;
import javax.xml.bind.annotation.XmlRegistry;
import gbooks.inv.BookType;
@XmlRegistry
public class RM_ObjectFactory extends gbooks.inv.ObjectFactory {
@Override
public BookType createBookType() {
return new RM_BookType();
}
}
Here is a program to demonstrate the use of these new classes. We have hardcoded the input file name, line 2. Line 4, the JAXBContext is defined with the jaxb generated classes as one normally would; the JAXBContext is not define pointing to the package with your subclasses. Line 6 identifies RM_ObjectFactory for jaxb to use. Line 8 we add a new book to the inventory. Lines 14 and 15 demonstrates the new functionality that is implemented.
1 public class Test {
2 public static final String XML_TEST_FILE = "BookList.xml";
3 public static void main(String[] args) throws Exception {
4 JAXBContext context = JAXBContext.newInstance("gbooks.inv");
5 Unmarshaller u = context.createUnmarshaller();
6 u.setProperty("com.sun.xml.bind.ObjectFactory",new RM_ObjectFactory());
7 BookInventory bi = (BookInventory)u.unmarshal(new File(XML_TEST_FILE));
//- add something new to the list
8 RM_BookType rmb = new RM_BookType();
9 rmb.setIsbin("9932-3423");
10 rmb.setTitle("Partial Math");
11 rmb.setPrice(0.40);
12 bi.getBook().add(rmb);
13 for(BookType b: bi.getBook()){
14 b.setIsbin("ACCT_");
15 System.out.println("isbin: " + b.getIsbin() + "\tprice: " +
b.getPrice());
}
}
}
Here is some sample data
BookList.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<BookInventory>
<Book>
<isbin>1209-01</isbin>
<title>New Math</title>
<price>40.00</price>
</Book>
<Book>
<isbin>2209-01</isbin>
<title>Old Math</title>
<price>4.00</price>
</Book>
</BookInventory>
The output should look like this.
isbin: ACCT_1209-01 price: 44.0
isbin: ACCT_2209-01 price: 4.4
isbin:ACCT_9932-3423 price:0.44000000000000006
A zip file containing the sources for this example can be found here
The provided ant file requires you set 3 properties in order to resolve several Jar file references.
Posted at 03:40PM Apr 16, 2007 by rsearls in Sun | Comments[0]
Monday Apr 16, 2007