users@jaxb.java.net

How to ignore/disable/revert/override JAXB class-level @XmlJavaTypeAdapter in certain situations ?

From: Charles Ouimet <charles.ouimet_at_gmail.com>
Date: Fri, 8 Nov 2013 10:10:05 -0500

Hello,

Our model classes are annotated with @XmlJavaTypeAdapter (at the
class-level). Unmarshalling works fine for the root element and
containment/nesting (according to what we implemented in our custom
XmlAdapter).

So far, we were happy campers for both XML and JSON
serialization/deserialization. However, a new need arose and I can't figure
out how to implement it ?

In certain situations, I'd like to be able to "revert" to default JAXB
behavior for containment: I want the class-level @XmlJavaTypeAdapter
annotation to be ignored/overriden.

I spent hours reading Blaise Doughan's blog (http://blog.bdoughan.com/) and
searching StackOverflow and Google but can't find an elegant/pragmatic
solution.

Here is a quick setup to illustrate what we currently have (please note
that all our JPA/Hibernate/other annotations are not listed for
simplicity-sake but they do exist in our model classes (POJOs)):

Class Master

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlJavaTypeAdapter(XmlMasterAdapter.class)
public class Master {
    @XmlElement
    private Long masterPrimaryKey;

    @XmlElement
    private String name;
}

Class Detail

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlJavaTypeAdapter(XmlDetailAdapter.class)
public class Detail {
    @XmlElement
    private Long detailPrimaryKey;

    @XmlElement
    private Master master; // reference/foreign key. No need for
@XmlJavaTypeAdapter since it's defined at the class-level in Master.

    @XmlElement
    private String value;
}

When Master is used as a the root element, the XML is like this:

<master>
    <masterPrimaryKey>1234</masterPrimaryKey>
    <name>master name</name>
</master>
When Master is used as a contained/nested element, the XML is like this:
(thanks to our custom XmlAdapter, the <master> element is "summarized" by
its primary key)

<detail>
    <detailPrimaryKey>5678</detailPrimaryKey>
    <master>1234</master>
    <value>detail value</value>
</detail>

So far, everything works fine and we're happy with it.

Now, our new need:

I'd like containment to work in a different way in specific situations. I
want the class-level @XmlJavaTypeAdapter on Master to "temporarily" be
ignored/reverted/overridden in a specific context. I'd expect the default
JAXB unmarshaller to kick-in (as if there had never been a class-level
@XmlJavaTypeAdapter on the contained classes).

Think about a data-import situation where we receive the master and all the
details in one payload. As if they were all independent root elements
wrapped in a big DTO/transport container.

Here is the XML presenting what we want:

<masterDetailImport>
    <master>
        <!-- Primary keys omitted because of the import mode -->
        <name>master name</name>
    </master>

    <details>
        <detail>
            <value>detail 1 value</value>
        </detail>

        <detail>
            <value>detail 2 value</value>
        </detail>

        <detail>
            <value>detail 3 value</value>
        </detail>
    </details>
</masterDetailImport>

Class MasterDetailImport

@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
public class MasterDetailImport implements Serializable
{
    @XmlElement

@PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
    private Master master;

    @XmlElementWrapper(name="details")
    @XmlElement

@PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
    private List<Detail> detail = new ArrayList<Detail>();
}

What I'm looking for is the magic [yet non-existing]
@PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
annotation that would allow me to instruct JAXB to do as if
@XmlJavaTypeAdapter had never been defined at the class-level for the
nested classes.

So far, the solutions we envisioned [and don't like] are:

1. Create "mirror" DTO objects for deserialization only when we must
support import. The are many cons with this approach (duplicate code only
used for deserialization, adapters to copy the DTO content into the model
class, more unit tests to write/maintain, etc).

2. Get rid of class-level @XmlJavaTypeAdapter on all our entities we want
to be able to import/nest and explicitly use @XmlJavaTypeAdapter on all
attributes where nesting/containment is used. I tested this approach and
know it would work. However, I think it's error prone and not as elegant as
defining it at class-level and be able to have an
exception/special-case/override handling telling JAXB to temporarily behave
as if it never knew @XmlJavaTypeAdapter has been defined on the class.

I'm running out of ideas here... I tried looking for JAXB's default XML
adapter but was not successful:
javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType> is
abstract and inherits from Java.lang.Object.

Now, the simple question: How to implement
@PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
?

PS: I posted the same question on StackOverflow (
http://stackoverflow.com/questions/19842179/how-to-ignore-disable-revert-override-jaxb-class-level-xmljavatypeadapter-in-ce),
I'm reaching to the mailing list to have a more specialized audience.

Thanks in advance !

-C