I've studied Blaise Doughan's answer to a question on this subject but have a further question.
XmlJavaTypeAdapters
lets you list a bunch of XmlJavaTypeAdapter
annotations, each of which governs how a non-bindable type is mapped to a bindable type by JAXB.
You can use this annotation at the package level. When you do so, every XmlJavaTypeAdapter
annotation needs its type()
attribute fully specified.
There does not appear to be a requirement that the package that is being annotated have anything to do with the package of the non-bindable types being adapted. That is convenient and nice.
That, however, leads me to my next question: if there is no relationship between the annotated package and the package of the type being adapted, how does JAXB discover package-level XmlJavaTypeAdapters
annotations? How, in other words, does it know which packages to consult for potential XmlJavaTypeAdapters
annotations? May I make a random package in, say, a .jar
file in my .ear
file's lib
directory that contains a single, ginormous package-info
class that is annotated with all the adapters for all of my non-bindable types?
When the JAXB runtime loads a JAXB-annotated class, it looks for a package-info.java
in the same package as that class, and checks that to look for package-level annotations. So while XmlJavaTypeAdapters
doesn't have to reside in the same package as the "non-bindable" types, it does have to reside in the same package as the "bindable" types.
For example, say I have a JAXB-annotated class A
, in package X
, which has a property of type B
in package Y
. In order to bind the B
property, let's say a type adapter is required. That adapter can be specified in A
itself, or it can be specified in the package-info.java
in package X
. Package Y
is pretty much arbitrary, and is of no interest to the JAXB runtime.
I hope that makes sense.
There does not appear to be a requirement that the package that is being annotated have anything to do with the package of the non-bindable types being adapted. That is convenient and nice.
This is correct. When @XmlJavaTypeAdapter
is used at the package level it means apply this adapter to all properties of the specified type for classes that reside in this package. I'll demonstrate below with an example.
forum8735737.bar.package-info
For this package we will specify an XmlAdapter
that will be applied to all fields/properties of type String
within this package.
@XmlJavaTypeAdapters({
@XmlJavaTypeAdapter(value=StringAdapter.class, type=String.class)
})
package forum8735737.bar;
import javax.xml.bind.annotation.adapters.*;
forum8735737.bar.StringAdapter
Our XmlAdapter
will simply convert all instances of String
to upper case when marshalling:
package forum8735737.bar;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class StringAdapter extends XmlAdapter<String, String> {
@Override
public String unmarshal(String v) throws Exception {
return v;
}
@Override
public String marshal(String v) throws Exception {
if(null == v) {
return v;
}
return v.toUpperCase();
}
}
forum8735737.bar.Bar
Bar
represents a POJO in this package with a property of type String
:
package forum8735737.bar;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Bar {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
forum8735737.foo.Foo
Foo represents a domain object with a property of type String
that exists in a different package. The XmlAdapter
we registered for the forum8735737.bar
package will not apply to this class:
package forum8735737.foo;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Foo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Demo
The following code will create instances of both Foo
and Bar
and marshal them to XML:
package forum8735737;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import forum8735737.bar.Bar;
import forum8735737.foo.Foo;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class, Bar.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
Foo foo = new Foo();
foo.setName("Foo");
marshaller.marshal(foo, System.out);
Bar bar = new Bar();
bar.setName("Bar");
marshaller.marshal(bar, System.out);
}
}
Output
Notice how the value of the name
element within bar
has been converted to upper case:
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<name>Foo</name>
</foo>
<?xml version="1.0" encoding="UTF-8"?>
<bar>
<name>BAR</name>
</bar>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With