blob: b73715dd29efe22902b04def6613e4ad4383cac2 [file] [log] [blame]
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Distribution License v. 1.0, which is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: BSD-3-Clause
-->
<!DOCTYPE book [
<!ENTITY % ents SYSTEM "docbook.ent">
%ents;
]>
<section version="5.0"
xml:id="annotating-your-classes-xml-layout-and-in-memory-data-layout"
xml:lang="en" xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:ns5="http://www.w3.org/1999/xhtml"
xmlns:ns3="http://www.w3.org/2000/svg"
xmlns:ns="http://docbook.org/ns/docbook"
xmlns:m="http://www.w3.org/1998/Math/MathML">
<title>XML layout and in-memory data layout</title>
<para>Your program sometimes needs to have a different in-memory data
structure from its XML representation. &binding.spec.name; has a few different ways to
achieve this.</para>
<section xml:id="XmlJavaTypeAdapter">
<title>XmlJavaTypeAdapter</title>
<para><literal>XmlJavaTypeAdapter</literal> allows you to de-couple the
in-memory representation and the XML representation by introducing an
intermediate representation. The basic model is as follows:</para>
<informalexample>
<programlisting language=""><![CDATA[In-memory objects <===> Intermediate objects <===>
XML
adapter XMLBinding]]></programlisting>
</informalexample>
<para>Your adapter code will be responsible for converting in-memory
objects to/from intermediate objects. Intermediate objects are then
bound to XML by following the standard &binding.spec.name; rules. See <literal>XmlAdapter</literal> for a general description of how
adapters works.</para>
<para>Adapters extend from the <literal>XmlAdapter</literal> class and provide two methods
"unmarshal" and "marshal" that converts values in both directions, and
then the <literal>XmlJavaTypeAdapter</literal> annotation is used to tell
&binding.spec.name; where and what adapters kick in.</para>
<para>(TODO: more info about XmlJavaTypeAdapter needed)</para>
<orderedlist>
<listitem>
<para>adapting a class</para>
</listitem>
<listitem>
<para>adapting a property</para>
</listitem>
<listitem>
<para>adapting an external class</para>
</listitem>
<listitem>
<para>adapting a collection and its effect</para>
</listitem>
<listitem>
<para>adapting and using interfaces</para>
</listitem>
</orderedlist>
</section>
<section xml:id="Using_XmlJavaTypeAdapter_for_element_attribute_values">
<title>Using XmlJavaTypeAdapter for element/attribute values</title>
<para>One of the common use cases of <literal>XmlJavaTypeAdapter</literal> is to map a "value object" to
a string in XML. The following example illustrates how to do this, by
using <literal>java.awt.Color</literal> as an example.</para>
<example>
<title>Mapping Color to #RRGGBB</title>
<programlisting language="java"><![CDATA[@XmlRootElement
class Box {
@XmlJavaTypeAdapter(ColorAdapter.class)
@XmlElement
Color fill;
}
class ColorAdapter extends XmlAdapter<String,Color> {
public Color unmarshal(String s) {
return Color.decode(s);
}
public String marshal(Color c) {
return "#"+Integer.toHexString(c.getRGB());
}
}]]></programlisting>
</example>
<para>This maps to the following XML representation:</para>
<example>
<title>Box instance</title>
<programlisting language="xml"><![CDATA[<box>
<fill>#112233</fill>
</box>]]></programlisting>
</example>
<para>Since <literal>XmlJavaTypeAdapter</literal> is on a field, this adapter
only kicks in for this particular field. If you have many
<literal>Color</literal> fields and would like them all to use the same
adapter, you can move the annotation to a package:</para>
<example>
<title>package-info.java</title>
<programlisting language="java"><![CDATA[@XmlJavaTypeAdapter(type=Color.class,value=ColorAdapter.class)
package foo;]]></programlisting>
</example>
<example>
<title>Box.java</title>
<programlisting language="java"><![CDATA[@XmlRootElement
class Box {
@XmlElement Color fill;
@XmlElement Color border;
}]]></programlisting>
</example>
<para>This causes all the fields in the classes in the
<literal>foo</literal> package to use the same specified adapter.</para>
<para>Also see the <literal>DatatypeConverter</literal> class that defines a
series of basic conversion routines that you may find useful.</para>
</section>
<section xml:id="Pair_property">
<title>Pair property</title>
<para>Another useful technique is to define two properties, one for
&binding.spec.name; and the other for your application. See the following
example:</para>
<example>
<title>Pair property sample</title>
<programlisting language="java"><![CDATA[@XmlRootElement
class Person {
private int age;
// This public property is for users
@XmlTransient
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// This property is for Jakarta XML Binding
@XmlAttribute(name="age")
private String getAge_() {
if(age==-1) return "dead";
else return String.valueOf(age);
}
private void setAge_(String v) throws NumberFormatException {
if(v.equals("dead")) this.age=-1;
else this.age=Integer.parseInt(age);
}]]></programlisting>
</example>
<para>The main "<literal>age</literal>" property is public, but marked as <literal>XmlTransient</literal>, so it's exposed in your program,
but &binding.spec.name; will not map this to XML. There's another private "<literal>age_</literal>"
property. Since this is marked with <literal>XmlAttribute</literal>, this is what &binding.spec.name; is going to use
to map to the attribute. The getter and setter methods on this
property will handle the conversion between the in-memory
representation and the XML representation.</para>
</section>
</section>