prev | table of contents | next |
In the previous section we have tacitly assumed that there is one and only
one object with a certain identification which is readily available from
the object. But what do you do if there is no such identification?
Generating synthetic identifications isn't a problem, but how does
this help to avoid generating full XML text for each occurrence? Should
we use the equals(Object o)
method for identifying
objects that deserve the same synthetic identifier? The answer is that
with a little extra effort you can still preserve object identity
so that the same number of objects can be reconstructed when the XML data
is read and unmarshalled back into memory.
Object identity can be tested by applying the operator ==
to any two objects. Keeping a list of marshalled elements and searching
through it isn't attractive. Luckily there is a better way: we can use
an instance of an IdentityHashMap
(from java.util
)
to register marshalled elements. This map uses the object's default hash code,
even if hashCode
has been overridden. Also, we have to
add an (artificial) identification to our objects as this is required
as the xsd:ID
value.
For an example we extend the schema describing orders with an additional
AddressType
and use this, once for a shipping address and
once for a billing address.
<xsd:complexType name="AddressType"> <xsd:sequence> <xsd:element name="street" type="xsd:string"/> <xsd:element name="city" type="xsd:string"/> <xsd:element name="country" type="xsd:string"/> <xsd:element name="zip" type="xsd:int"/> </xsd:sequence> <xsd:attribute name="id" type="xsd:ID"/> </xsd:complexType> <xsd:complexType name="AddrOrRefType"> <xsd:choice> <xsd:element name="addr" type="AddressType"/> <xsd:element name="addrRef" type="xsd:IDREF"/> </xsd:choice> </xsd:complexType> <xsd:complexType name="OrderType"> <xsd:sequence> <xsd:choice> <xsd:element name="customer" type="CustomerType"/> <xsd:element name="custref" type="xsd:IDREF"/> </xsd:choice> <xsd:element name="shipTo" type="AddrOrRefType"/> <xsd:element name="billTo" type="AddrOrRefType"/> <xsd:element name="items" type="ItemType" maxOccurs="unbounded"/> </xsd:sequence> </xsd:complexType>The generated class
AddrOrRefType
enables us to choose
between an address in full or a reference to such an XML element.of
type AddressType
. To see how this works, we assume that
we have addresses in objects of type Address
. The code
below creates another XML element for AddressType
from
an Address
object.
Map<Address,AddressType> pojo2elem = new IdentityHashMap<Address,AddressType>(); int refcount = 0; private String makeNextId(){ return "a" + refcount++; } public AddrOrRefType makeAddrOrRefElement( ObjectFactory objFact, Address addrPojo ){ AddrOrRefType arElem = objFact.createAddrOrRefType(); AddressType addrElem = pojo2elem.get( addrPojo ); if( addrElem == null ){ // First time: generate the full XML element... addrElem = objFact.createAddressType(); // ...and insert it into its parent. arElem.setAddr( addrElem ); // Set the xsd:ID attribute. addrElem.setId( makeNextId() ); // ...(Copy attributes from addrPojo into addrElem.)... // Register the object - id pair in the identity hash map. pojo2elem.put( addrPojo, addrElem ); } else { // We've had this one before: insert its reference. arElem.setAddrRef( addrElem ); } return arElem; }Using
Address
objects as keys, we map these to assembled
AddressType
objects. Whenever we encounter a new
object of class Address
, we enter it into the map
and generate an AddrOrRef
element containing the full
AddressType
element. For an encore, the AddrOrRef
receives the reference to the previously created AddressType
element.
prev | table of contents | next |