I had an instance once where we were supporting a GDS's XML format for communicating hotel and airlines data. To make parsing the extensive amount of XML, we used castor to marshal and unmarshal the XML to our Java models. Castor, as an XML marshalling library, is great! But sometimes there are instances where you want more control over your XML marshalling and unmarshalling.
The format of the XML (which will remain unnamed) was in such a way where the order of the elements mattered. The XML format felt like it was rushed by the vendor but there was nothing we could do to change that. Fortunately castor supports marshaling listeners.
For example, to unmarshal, castor has the UnmarshalListener interface which you can implement and apply whenever you wish to have more control over the marshaling process or keep track whenever marshaling takes place.
Four methods need to be implemented using this interface.
public void initialized(Object arg0)
public void attributesProcessed(Object arg0)
public void fieldAdded(String arg0, Object parent, Object child)
public void unmarshalled(Object object)
Like I said above, I had a problem where items had to be dealt with sequentially.
For example:
A
item which holds description of a hotel room might follow up immediately with a element which holds the elements. To complicate it further, some might not come with a etc.
So the solution was to use the UnmarshalListener to keep track whenever a Rate or Description item is being processed and then, in our Parent object, keep track of the index of the XML being processed and manually insert the descriptions/rates.
Example:
public void fieldAdded(String arg0, Object parent, Object child)
{
if (arg0.equals("XMLHotelItem"))
{
GwsHotelAvailabilityResponse hotelAvailability = (GwsHotelAvailabilityResponse) parent;
hotelAvailability.incrementErrorIndex();
GwsHotelInsideShopRate insideShopRate = new GwsHotelInsideShopRate ();
insideShopRate.setErrorIndex(hotelAvailability.getCurrentErrorIndex());
hotelAvailability.insertHotelInsideShopRate (insideShopRate);
}
// Hotel Description Handling
if (arg0.equals("hotelRates"))
{
GwsHotelCompleteAvailabilityResponse parentResponse = (GwsHotelCompleteAvailabilityResponse) parent;
parentResponse.incrementIndex();
} else
if (arg0.equals("descriptions"))
{
GwsHotelCompleteAvailabilityResponse parentResponse = (GwsHotelCompleteAvailabilityResponse) parent;
parentResponse.insertDescriptionString((String) child);
}
}
public void unmarshalled(Object object)
{
if (object instanceof GwsHotelCompleteAvailabilityResponse)
{
GwsHotelCompleteAvailabilityResponse response = (GwsHotelCompleteAvailabilityResponse) object;
// Set room master responses
if (response.getHotelTypeIndicator().equals("R"))
{
Iterator it = response.getHotelRates().iterator();
while (it.hasNext())
{
GwsHotelRate rate = (GwsHotelRate) it.next();
RoomMasterCodeTranslator trans = new RoomMasterCodeTranslator (rate.getBicCode());
rate.insertRoomTypeDescription(trans.getRoomQuality() + " room.");
rate.insertRoomTypeDescription(trans.getNumberOfBedsAsString() + " bed(s).");
rate.insertRoomTypeDescription(trans.getBedType() + " bed(s).");
}
}
}
}
}
To make use of the UnmarshalListener:
Mapping unmapping = new Mapping ();
unmapping.loadMapping(getClass().getResource(PATH_TO_CASTOR_MAPPING_FILE));
Unmarshaller un = new Unmarshaller (XmlResponse.class);
un.setUnmarshalListener(new CustomXmlUnmarshalListener());
un.setIgnoreExtraElements(true);
un.setMapping(unmapping);
StringReader sr = new StringReader (xml);
return (XmlResponse) un.unmarshal(sr);