Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "EclipseLink/UserGuide/MOXy/Mapping the Unmappable/Converters"

m (Replacing page with ''''Warning This page is obsolete. Please see ''[http://www.eclipse.org/eclipselink/documentation/ Developing JAXB Applications Using EclipseLink M...')
 
(8 intermediate revisions by one other user not shown)
Line 1: Line 1:
{{EclipseLink_UserGuide
+
'''[[Image:Elug_draft_icon.png|Warning]] This page is obsolete. Please see ''[http://www.eclipse.org/eclipselink/documentation/ Developing JAXB Applications Using EclipseLink MOXy]'' for current information.'''
|info=y
+
|toc=y
+
|eclipselink=y
+
|eclipselinktype=MOXy
+
|api=y
+
|apis= * [http://www.eclipse.org/eclipselink/api/latest/javax/xml/bind/annotation/adapters/XmlAdapter.html XmlAdapter]
+
* [http://www.eclipse.org/eclipselink/api/latest/javax/xml/bind/annotation/adapters/XmlJavaTypeAdapter.html XmlJavaTypeAdapter]
+
* [http://www.eclipse.org/eclipselink/api/latest/javax/xml/bind/annotation/adapters/XmlJavaTypeAdapters.html XmlJavaTypeAdapters]
+
}}
+
 
+
= XmlAdapter =
+
 
+
Some Java classes are not well suited for use with JAXB and at first glance may seem "unmappable" - for example, classes that do not have a default no-arg constructor, or classes for which an XML representation cannot be automatically determined.  Using JAXB's '''XmlAdapter''', you can define define custom code to convert the unmappable class into something that JAXB can handle.  Then, you can use the '''@XmlJavaTypeAdapter''' annotation to indicate that your adapter should be used when working with the unmappable class.
+
 
+
'''XmlAdapter''' uses the following terminology:
+
 
+
* ValueType - The type that JAXB knows how to handle out of the box.
+
* BoundType - The type that JAXB doesn't know how to handle. An adapter is written to allow this type to be used as an in-memory representation through the ValueType.
+
 
+
The outline of an '''XmlAdapter''' class is as follows:
+
 
+
<source lang=java">
+
package example;
+
 
+
import javax.xml.bind.annotation.adapters.XmlAdapter;
+
 
+
public class ''AdapterName'' extends XmlAdapter<''ValueType'', ''BoundType''> {
+
 
+
  public ''BoundType'' unmarshal(''ValueType'' value) throws Exception {
+
      ...
+
  }
+
 
+
  public ''ValueType'' marshal(''BoundType'' value) throws Exception {
+
      ...
+
  }
+
 
+
}
+
</source>
+
 
+
 
+
== Example - java.util.Currency ==
+
 
+
Our first example will use the following domain class:
+
 
+
<source lang="java">
+
package example;
+
 
+
import java.util.Currency;
+
 
+
import javax.xml.bind.annotation.*;
+
 
+
@XmlRootElement
+
@XmlAccessorType(XmlAccessType.FIELD)
+
public class PurchaseOrder {
+
 
+
  private Double amount;
+
 
+
  private Currency currency;
+
 
+
  ...
+
}
+
</source>
+
 
+
Here, the '''Currency''' cannot be automatically mapped with JAXB because it does not contain a no-argument constructor.  However, we can write an adapter that will convert the '''Currency''' into something that JAXB does know how to handle - a simple '''String'''.  Luckily, in this case the '''Currency's''' toString() method returns the currency code, which can also be used to create a new '''Currency''':
+
 
+
<source lang="java">
+
package example;
+
 
+
import java.util.Currency;
+
 
+
import javax.xml.bind.annotation.adapters.XmlAdapter;
+
 
+
public class CurrencyAdapter extends XmlAdapter<String, Currency> {
+
 
+
  /*
+
    * Java => XML
+
    * Given the unmappable Java object, return the desired XML representation.
+
    */
+
  public String marshal(Currency val) throws Exception {
+
      return val.toString();
+
  }
+
 
+
  /*
+
    * XML => Java
+
    * Given an XML string, use it to build an instance of the unmappable class.
+
    */
+
  public Currency unmarshal(String val) throws Exception {
+
      return Currency.getInstance(val);
+
  }
+
 
+
}
+
</source>
+
 
+
To indicate that our adapter should be used for the '''Currency''' property, we annotate it with '''@XmlJavaTypeAdapter''' and provide the class name of our adapter:
+
 
+
<source lang="java">
+
package example;
+
 
+
import java.util.Currency;
+
 
+
import javax.xml.bind.annotation.*;
+
 
+
@XmlRootElement
+
@XmlAccessorType(XmlAccessType.FIELD)
+
public class PurchaseOrder {
+
 
+
  private Double amount;
+
 
+
  @XmlJavaTypeAdapter(CurrencyAdapter.class)
+
  private Currency currency;
+
 
+
  ...
+
}
+
</source>
+
 
+
 
+
== Example - java.awt.Point ==
+
 
+
Sometimes the best way to handle an unmappable class is to write a "stand-in" class which ''can'' be mapped with JAXB, and convert between the two classes in the '''XmlAdapter'''.  In this example, we want to use the '''Point''' class.  Because of that class' '''getLocation()''' method (which JAXB will pickup automatically and map), an infinite loop will occurr during marshalling.  Because we cannot change the '''Point''' class, we will write a new class, '''MyPoint''', and use it in the adapter.
+
 
+
<source lang="java">
+
package example;
+
 
+
public class MyPoint {
+
 
+
  private int x, y;
+
 
+
  public MyPoint() {
+
      this(0, 0);
+
  }
+
 
+
  public MyPoint(int x, int y) {
+
      this.x = x;
+
      this.y = y;
+
  }
+
 
+
  public int getX() {
+
      return x;
+
  }
+
 
+
  ...
+
}
+
</source>
+
 
+
<source lang="java">
+
package example;
+
 
+
import java.awt.Point;
+
 
+
import javax.xml.bind.annotation.adapters.XmlAdapter;
+
 
+
public class MyPointAdapter extends XmlAdapter<MyPoint, Point> {
+
 
+
  /*
+
    * Java => XML
+
    */
+
  public MyPoint marshal(Point val) throws Exception {
+
      return new MyPoint((int) val.getX(), (int) val.getY());
+
  }
+
 
+
  /*
+
    * XML => Java
+
    */
+
  public Point unmarshal(MyPoint val) throws Exception {
+
      return new Point(val.getX(), val.getY());
+
  }
+
 
+
}
+
</source>
+
 
+
Finally, our '''Point''' properties are marked with '''@XmlJavaTypeAdapter''':
+
 
+
<source lang="java">
+
package example;
+
 
+
import java.awt.Point;
+
 
+
import javax.xml.bind.annotation.*;
+
 
+
@XmlRootElement
+
@XmlAccessorType(XmlAccessType.FIELD)
+
public class Zone {
+
 
+
  private String name;
+
 
+
  @XmlJavaTypeAdapter(MyPointAdapter.class)
+
  private Point startCoord;
+
 
+
  @XmlJavaTypeAdapter(MyPointAdapter.class)
+
  private Point endCoord;
+
 
+
  ...
+
}
+
</source>
+
 
+
 
+
== Specifying Package-Level Adapters ==
+
 
+
In the example above, we annotated both '''Point''' properties with the '''@XmlJavaTypeAdapter''' annotation.  If you have many of these types of properties - for example, in other domain classes - it can be more convenient to specify the '''@XmlJavaTypeAdapters''' at the package level.
+
 
+
We could define both of the adapter classes in '''package-info.java''', and then we would not have to annotate any further '''Currency''' or '''Point''' properties:
+
 
+
<source lang="java">
+
 
+
@XmlJavaTypeAdapters({
+
  @XmlJavaTypeAdapter(value=CurrencyAdapter.class,type=Currency.class),
+
  @XmlJavaTypeAdapter(value=MyPointAdapter.class,type=Point.class)
+
})
+
package example;
+
</source>
+
 
+
 
+
== Class-Level @XmlJavaTypeAdapters ==
+
 
+
If you have a Java class and you would like to always use an '''XmlAdapter''' during marshalling and unmarshalling, then you can specify the '''@XmlJavaTypeAdapter''' directly at the class level:
+
 
+
<source lang="java">
+
package example;
+
 
+
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
+
 
+
@XmlJavaTypeAdapter(DataStructureAdapter.class)
+
public class DataStructure {
+
 
+
  ...
+
 
+
}
+
</source>
+
 
+
Now, any object that has a '''DataStructure''' property will automatically use the '''DataStructureAdapter''', without the need for an annotation on the property itself.
+

Latest revision as of 13:19, 30 January 2013

Warning This page is obsolete. Please see Developing JAXB Applications Using EclipseLink MOXy for current information.

Back to the top