users@jaxb.java.net

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

From: Markus Karg <markus.karg_at_gmx.net>
Date: Fri, 18 Dec 2009 08:51:16 +0100

Wolfgang,

 

(a) Can you please point me to that section of the JAXB 2.x specification
saying that @XmlJavaTypeAdapter and @XmlRootElement is not compatible?
(Happily in my particular case, <e> is not a document root, so it works
well)

 

(b) The problem is not third party code creating wild instances, but JAXB
creating similar copies while unmarshalling third party code. I do not see a
way to prevent this, as there is no way to force third party authors to
overwrite "unmarshal()" when referencing instances of <e>.

 

Thanks

Markus

 

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

 

So I should caution you that @XmlJavaTypeAdapter and @XmlRootElement are not
compatible. The adapter is not called if <e> is a document root.

For classes such as ThirdPartyElement, which users contribute for
marshalling and
unmarshalling, you can force them to use factory methods
  public E newInstance(final V1 v1){
      return V_1;
  }
  public E newInstance(final V2 v2){
      return V_2;
  }
which won't let them create any wild E objects in the first place.

-W



On Thu, Dec 17, 2009 at 7:54 PM, Markus Karg <markus.karg_at_gmx.net> wrote:

Wolfgang,

 

I never said that your code is not working at all, but just the proposed
solution it outlines does not work in my particular case. The problem is
that third parties might use class E in conjunction with their own code, e.
g.

 

@XmlRootElement

public class ThirdPartyElement {

   public E e;

}

 

In that case, the third party author must take care to implement the
singleton support you are proposing. As a result, that singleton support
ends up in lots of places, and if only one of them are broken.: Happy
searching! ;-)

 

The cause is that the unmarshall-Callbacks cannot replace the current
object. It just can modify the object, or it can modify the parent, what
both is suboptimal.

 

The solution I just sent to the mailing list solves this problem, as the
singleton stuff in self-contained in the class E itself. So no third party
author has to take care, resulting in no code places which could break. For
me, this looks like the ultimate solution to the problem, but maybe you have
a better idea? :-)

 

Regards

Markus

 

From: Wolfgang Laun [mailto:wolfgang.laun_at_gmail.com]
Sent: Donnerstag, 17. Dezember 2009 18:45


To: users_at_jaxb.dev.java.net

Subject: Re: FW: How to map Java enum to XML element (not #PCDATA) body?

 

Markus,

this has to be discussed with a little more background. I understand that
you have an XML schema, which apparently defines <e> with a subordinate
choice with alternatives <v1/>,...<vN/>. No Java code is generated from this
schema; you write the class E by hand.

One would think that elements <e> occurs in the XML schema, and that's why I
wonder what these objects E might have to do with the API you are providing.
If you are providing unmarshalling for XML according to the schema, my
proposed solution works.

Marshalling is slightly different, but you haven't used this word yet ;-)

A short sample code of what your API must provide in connection with class E
might help.

-W



On Thu, Dec 17, 2009 at 3:40 PM, Markus Karg <markus.karg_at_gmx.net> wrote:

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