10

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?

Community
  • 1
  • 1
Laird Nelson
  • 15,321
  • 19
  • 73
  • 127

2 Answers2

9

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.

skaffman
  • 398,947
  • 96
  • 818
  • 769
  • Very helpful; thanks. Does the graph get walked from there? That is, suppose I have a single bindable type that I feed to JAXB. Suppose further that his package is annotated with 647 `XmlJavaTypeAdapter` annotations, each of which specifies a non-bindable type and an adapter for that type. Would my `JAXBContext` now be able to handle all 647 now-adapted types? – Laird Nelson Jan 05 '12 at 00:11
  • @Laird: Yes, as long as (a) you had the RAM to handle it, and (b) All of the bindable types were in that package. – skaffman Jan 05 '12 at 07:50
  • Interesting. Seems odd that I'd have to bootstrap the whole process, though. I mean, if I can set up a pile of adapters by simply feeding JAXB a single type, then I would hope it should be possible to improve JAXB so that that single type wouldn't be necessary either. Like have JAXB look by default for a "root" `package-info` somewhere so that the initial bootstrapping isn't necessary. Thanks again for your help. – Laird Nelson Jan 05 '12 at 13:34
5

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>
bdoughan
  • 147,609
  • 23
  • 300
  • 400