users@jaxb.java.net

Re: Supporting Maps

From: Kohsuke Kawaguchi <Kohsuke.Kawaguchi_at_Sun.COM>
Date: Mon, 14 Feb 2005 09:15:11 -0800

Brian Pontarelli wrote:
> I had an idea for JAXB customization. Currently elements that contain
> multiple sub-elements, those elements are stored in a List<Type> (Type
> being the type of the sub-element). In some cases, the sub-elmenets will
> have attributes that uniquely identify them from their siblings. Here's
> a snippet:
>
> <shoppingCart>
> <item id="42098209823" cost="40.50"/>
> <item id="20983098420" cost="75.00"/>
> </shoppingCart>
>
> If this is the case and the schema designer wishes to access these items
> as a Map they currently only have the option of sub-classes the JAXB
> generated class to provide a Map method. Could this be added into the
> customization sechema? I was thinking something like this:
>
> <jaxb:property name="item" collectionType="java.util.HashMap"
> keyAttribute="id"/>
>
> The methods would then look like this:
>
> public Map<String, Item> getItem() { ... }
>
> public void setItem(Map<String, Item>) { ... }
>
> Using a HashMap would loose the ordering of elements, but using a
> LinkedHashMap would retain the order they appear in the XML document.
> You could also explode the concept out so that duplicates could be
> stored correctly using a structure like this:
>
> Map<String, List<Item>>
>
> This could be controlled via a flag in the customization like this:
>
> <jaxb:property name="item" collectionType="java.util.HashMap"
> keyAttribute="id" retainDuplicates="true"/>
>
> What do you all think?

I agree that it's useful, but it's jut more work. A few questions:

- you have @keyAttribute, which seems to imply that elements can't be a
key. This is easier to implement, but do you have any justification for
not allowing elements to be keys?

- do you really need "retainDupliates"? If you expect duplicates, would
you really want to use a Map?


One possible easier (for us) approach would be to design the basics so
that you can add this kind of functionality by deriving/modifying a
generated class.

This is RI-specific, but there's the com.sun.xml.bind.ObjectLifecycle
interface you can implement on your object (see the attached) so that
you can get notified when your object is unmarshalled. This will allow
you to set up your own Map.

-- 
Kohsuke Kawaguchi
Sun Microsystems                   kohsuke.kawaguchi_at_sun.com


package com.sun.xml.bind;

import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

/**
 * JAXB-bound objects can implement this interface
 * to receive additional event callbacks from the runtime.
 *
 * <p>
 * This allows the object to perform additional processing
 * at certain key point in the unmarshalling/marshalling operation.
 *
 * <p>
 * Note that this interface is recognized only by the JAXB RI.
 * If your application relies on the events to be generated,
 * you have to make sure that you always use the JAXB RI at the runtime.
 *
 * TODO: think about a better name.
 *
 * @author Kohsuke Kawaguchi
 * @since JAXB RI 2.0
 */
/*
    Internally, the lifecycle method invocation is handled by SAXUnmarshallerImpl
*/
public interface ObjectLifecycle {
    /**
     * Called on the start of the unmarshalling.
     *
     * <p>
     * This event is fired before the unmarshalling of this object
     * begins. For the normal unmarshalling operation, this is
     * usually immediately after the object is created.
     *
     * @param unmarshaller
     * the unmarshaller in charge of this unmarshalling operation.
     * @param parent
     * if this object is a child object of another object,
     * this parameter points to the parent object to which
     * this object will be set.
     *
     * this parameter is null when this object is the root object.
     */
    void beforeUnmarshalling( Unmarshaller unmarshaller, Object parent );

    /**
     * Called on the completion of the unmarshalling.
     *
     * <p>
     * This event is fired after all the properties (except IDREF)
     * are unmarshalled, but before this object is set to the parent
     * object.
     *
     * <p>
     * This event does not mean that the unmarshalling of the whole
     * XML document has finished; it just means that the unmarshalling
     * of this object (plus all its descendants except IDREF) has finished.
     *
     * @param unmarshaller
     * the unmarshaller in charge of this unmarshalling operation.
     * @param parent
     * if this object is a child object of another object,
     * this parameter points to the parent object to which
     * this object will be set.
     *
     * this parameter is null when this object is the root object.
     */
    void afterUnmarshalling( Unmarshaller unmarshaller, Object parent );

    /**
     * Called on the start of the marshalling.
     *
     * <p>
     * This event is fired before the marshalling of this object starts.
     *
     * @param marshaller
     * the marshaller in charge of this marshalling operation.
     */
    void beforeMarshalling( Marshaller marshaller );

    /**
     * Called on the end of the marshalling.
     *
     * <p>
     * This event is fired after the marshalling of this object
     * (and all its descendants) has finished.
     *
     * @param marshaller
     * the marshaller in charge of this marshalling operation.
     */
    void afterMarshalling( Marshaller marshaller );
}