Ryan's Blog : Weblog

Ryan's Blog

Friday Mar 21, 2008

JiBX Flexible Collections

In a previous post I talked about some of the difficulties I had with JiBX.  I was using JiBX to map to the vendor neutral Acord Life Insurance schemas.  One recurring theme in the Acord schemas is repeating elements without a wrapping tag:

<tag>

    <tagA/>

    <tagB/>
</tag>

<tag>

    <tagA/>

    <tagB/>

</tag>

A downside of using Acord is that there are so many tags (since it has to fit so many business models and domains) that it is not feasible to handle every possible tag in Acord.  To allow this, JiBX has an attribute called "flexible" and "ordered" that will skip tags that are in the XML, but are not bound (the default behavior is to throw an exception when this happens) and will allow tags in any order.  Since I didn't want to write code for the thousands of tags, I set the flexible attribute to true and the ordered attribute to false.  The problem happens where theres a repeating tag like the one above, used with the flexible feature.  Once the tags are marked flexible, JiBX complains with this error:

[bind] Error: All child components must define element names for flexible='true'

In this mailing list post form the JiBX creator, this is apparently a known issue: http://marc.info/?l=jibx-users&m=118584611515298&w=2.
The options are to modify the message format or create a custom marshaller. I am not able to modify the Acord message format,
so I was left with a custom marshaller. I wasn't very happy with that idea, since it meant that I would basically be hand parsing the XML (the exact
thing I was trying to avoid by using JiBX). I tried a couple of solutions and what seemed to be the best was a hybrid approach of a custom marshaller,
and a JiBX marshaller. To do this I created a JiBX mapping for that class that I was going to write the custom marshaller for. It looked something like this:

<mapping prefix="ns" uri="http://namespace/here" default="none" class="com.company.product.Address" name="Address">
<value name="AddressLine1" field="addressLine1" usage="optional" />
<!--...-->
</mapping>

 
This is a normal JiBX mapping, the only difference is that although the mapping is defined, it isn't connected to anything. Technically
this should have been reference in the "Person" structure, since, for this example that is where the Addresses reside. Instead

of putting the reference to the Address in the person, I deferred that to the custom marshaller:

 <structure field="addresses" name="Address" usage="optional" marshaller="com.company.product.AddressMarshaller" unmarshaller="com.company.AddressMarshaller"/>

This reference doesn't reference a collection. It just references a single address and then the marshaller takes care of the rest.
Now the marshalling and unmarshalling code looks like:


public void marshal(Object addresses, IMarshallingContext ctx) throws JiBXException {

MarshallingContext mctx = (MarshallingContext)ctx;
List addressList = (List) addresses;


for(Iterator it = addressList.iterator();it.hasNext();){
((IMarshallable)it.next()).marshal(mctx);
}

}

public Object unmarshal(Object obj, IUnmarshallingContext uctx) throws JiBXException {

UnmarshallingContext unmarshalContext = (UnmarshallingContext)uctx;
List list = (List) obj;

if (list == null) {
list = new ArrayList();
}

while (unmarshalContext.isAt(getUri(), getName())) {
list.add(unmarshalContext.unmarshalElement(Address.class));
}

return list;
}

 

Note that in the above code there is no hand parsing of the XML elements. Instead the actual marshalling and unmarshalling of the
individual Address tags are deferred to JiBX. Basically the custom marshaller is just finding the one or more Address tags
and deferring the marshalling/unmarshalling of those tags to the regular JiBX marshaller/unmarshaller.

This allows unordered/flexible mappings to collections that do not have a wrapping tag. I was able to genericize this approach a
little more and created custom mappings for HashMaps and a couple of other structures that were useful handling the Acord documents.

 

 

Comments:

Thanks for this post. I've needed exactly the same thing from JiBX, and your solution works great... except, I'm not able to get the namespace to output onto each enclosing tag during marshalling.

In my scenario, I have a collection in different namespace than the document's default. So the output tag for each collection item needs to have namespace prefixes in them, e.g.:

<document xmlns="urn:defaultNamespace xmlns:ns0="urn:collectionNamespace>
<field1>value</field1>
<field2>value</field2>
<ns0:item>collection item 1</ns0:item>
<ns0:item>collection item 2</ns0:item>
<ns0:item>collection item 3</ns0:item>
</document>

I've tried a bunch of variations on both the <mapping> and <structure> elements in the binding (ns/uri/prefix attributes, <namespace> elements, etc.) but no matter what I try, the tags in the output document end up without namespace prefixes.

Have you seen this problem and/or found a way to make this work?

Posted by Nick on March 25, 2008 at 08:47 PM CDT #

Sorry it's taken me a while to get back to you. I've not needed to do this, but I just ran through a test case and know how I would probably go about it.

In the mapping that you define your tags in, put in there the namespace declaration.

<namespace prefix="ns3" uri="http://alternate/ns/3" default="none"/>

default="none" means that it's defined, but it won't be applied to any elements unless you specify them. Then below, you can define it like this:

<value name="SomeAlternateTag" field="fieldInClass" ns="http://ACORD.org/Standards/Life/3"/>

the ns="..." will tell jibx for that tag, to use that namspace. Hopefully that will work for you!

Posted by Ryan Senior on April 04, 2008 at 11:48 AM CDT #

Post a Comment:
  • HTML Syntax: NOT allowed

Calendar

Feeds

Search

Links

Navigation