Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What package-info do I annotate with XmlJavaTypeAdapters?

Tags:

java

jaxb

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?

like image 779
Laird Nelson Avatar asked Jan 04 '12 23:01

Laird Nelson


2 Answers

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.

like image 73
skaffman Avatar answered Oct 17 '22 22:10

skaffman


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>
like image 26
bdoughan Avatar answered Oct 17 '22 21:10

bdoughan