users@jaxb.java.net

RE: How to map Java enum to XML element (not #PCDATA) body?

From: Markus Karg <markus.karg_at_gmx.net>
Date: Thu, 17 Dec 2009 17:08:54 +0100

Meanwhile I found a solution and think that it seems to be quite optimal,
see below.

 

Any comments appreciated (maybe still too complex?)! :-)

 

@XmlAccessorType(FIELD)

@XmlJavaTypeAdapter(E.class)

@XmlRootElement()

public final class E extends XmlAdapter<E, E> {

  public static final E V_1 = new E(V1.SINGLETON, null);

  public static final E V_2 = new E(null, V2.SINGLETON);

 

  private E v1;

  private E v2;

 

  // Singleton

  private E() {

    // For unmarshalling only.

  }

 

  // Enum

  private E(final V1 v1, final V2 v2) {

    this.v1 = v1;

    this.v2 = v2;

  }

 

  @Override

  public final E marshal(final E v) throws Exception {

    return v;

  }

 

  @Override

  public final E unmarshal(final E v) throws Exception {

    if (v.v1 != null)

      return V_1;

    if (v.v2 != null)

      return V_2;

    return v;

  }

}

 

A @XmlRootElement that itself is it's own @XmlJavaTypeAdapter and
XmlAdapter, mapping itself to itself, actually is nothing else but an
"(Un)marshaller.Listener on steroids": It is able to replace instances of
itself by other instances on iteself at both, marshalling and unmarshalling.
That trick allows to implement not only highly complex enums, but also any
kind of value-aware (!) factories, like here, a multi-singleton. :-)

 

Regards

Markus

 

From: Markus Karg [mailto:markus.karg_at_gmx.net]
Sent: Donnerstag, 17. Dezember 2009 15:41
To: users_at_jaxb.dev.java.net
Subject: FW: How to map Java enum to XML element (not #PCDATA) body?

 

Anybody having an idea how to solve this?

 

From: Markus Karg [mailto:markus.karg_at_gmx.net]
Sent: Dienstag, 15. Dezember 2009 21:33
To: 'users_at_jaxb.dev.java.net'
Subject: RE: How to map Java enum to XML element (not #PCDATA) body?

 

Wolfgang,

 

your proposed solution unfortunately does not work! :-(

 

You are assuming that I know locations of any code that is using my enum.
But this is not the case. I am writing a library that anybody can use. The
job of providing a singleton has to be done *inside* my library (i. e. JAXB
itself must know that an instance has to be replaced by another instance
*before* providing the reference to a using instance), not *outside* (not in
the caller's class). Otherwise each user would have to know about the
singleton problem and take care of it, what is very, very error prone.

 

I need a solution that works *within* my library. Somethink like an
"Unmarshal Resolver". I know I can register unmarshal listeners, but how to
tell in that's event handler that the outcome is a *different* instance?

 

Thanks!

Markus

 

From: Wolfgang Laun [mailto:wolfgang.laun_at_gmail.com]
Sent: Dienstag, 15. Dezember 2009 20:15
To: users_at_jaxb.dev.java.net
Subject: Re: How to map Java enum to XML element (not #PCDATA) body?

 

What you want is a unique instance of E for each of the unique settings of
its fields v1, v2,... with a unique instance of V1, V2,..., respectively.

Since you are not compiling the Java classes from the XML schema, you could
write all setters of fields of type E so that

public void setE( E e ){
    this.e = E.getSingleton( e );
}

E.getSingleton() would have to determine which of e's fields v1, v2,... is
not null and return the corresponding E.V_1, E.V_2,...

-W

On Tue, Dec 15, 2009 at 5:58 PM, Markus Karg <markus.karg_at_gmx.net> wrote:

Using @XmlEnumValue it is possible to map Java enums to XML elements, like
in this example>

 

@XmlRootElement

public enum E {

  @XmlEnumValue("v1") V_1,

  @XmlEnumValue("v2") V_2,

  .

}

 

While this is working fine in case of #PCDATA values, I now am experiencing
the problem that I need to map values that are not #PCDATA but in fact are
XML elements:

 

For example,

 

<e><v1/></e>

 

shall result in the same Java enum instance than

 

<e>

    <v1>

    </v1>

</e>

 

and so on.

 

That means, I cannot write

 

@XmlEnumValue("<v1/>") V_1,

@XmlEnumValue("<v2/>") V_2,

 

in Java, because that would not result in an instance of B when
unmarshalling a formatted XML file.

 

Can anybody tell me a solution working in JAXB 2.1 that will solve this
problem?

 

I started with a rather complex workaround like this one (derived from the
singleton pattern):

 

@XmlAccessorType(FIELD)

@XmlRootElement

public class E {

  public static final E V_1 = new E(new V1());

  public static final E V_2 = new E(new V2());

  private V1 v1;

  private V2 v2;

  private E() { }

  private E(V1 v1) { this.v1 = v1; }

  private E(V2 v2) { this.v2 = v2; }

}

 

@XmlRootElement

public final class V1 { }

 

@XmlRootElement

public final class V2 { }

 

This perfectly unmarshalls any kind of formatting and whitespacing of
<e><v1/></e> and I am really happy with it so far, BUT it does not produce
singletongs. Instead, I am getting another instance of E and V1 each time.
This is rather bad, since I now must use the equals operator to compare
them, while it would be certainly great to in fact have the same instances
to be able to compare references:

 

if (unmarshalledObject == E.V1)

 

So my questions are:

 

* How can I get singletons, i. e. always the same instance of E.V1 and E.V2
instead of new copies for each reference?

 

* Ain't there a simpler way to get enums with dynamically formatted and
whitespaced XML element bodies?

 

BTW, since the XML schema is far out of my control, I am sorry, no, I cannot
change the fact that the body is not a #PCDATA but an XML element.

 

Thanks a lot!

Markus