I am using @XmlID and @XmlIDREF tags to reference one object from another. It was working fine in Java 6 even with inherited classes. The example code I've created looks like this. Base class used tags:
@XmlRootElement
@XmlAccessorType(FIELD)
public class Module {
Module() {}
@XmlIDREF
private Module other;
@XmlID
private String id;
public Module(String id, Module other) {
this.id = id;
this.other = other;
}
}
Inherited class:
@XmlRootElement
public class TheModule extends Module {
TheModule() {}
private String feature;
public TheModule(String id, Module other, String feature) {
super(id, other);
this.feature = feature;
}
}
Container for these classes:
@XmlRootElement
public class Script {
Script() {}
public Script(Collection<Module> modules) {
this.modules = modules;
}
@XmlElementWrapper
@XmlElementRef
Collection<Module> modules = new ArrayList<Module>();
}
When running this example code:
public class JaxbTest {
private Script createScript() {
Module m1 = new Module("Module1", null);
Module m2 = new TheModule("Module2", m1, "featured module");
Module m3 = new Module("Module3", m2);
return new Script(Arrays.asList(m1, m2, m3));
}
private String marshal(Script script) throws Exception {
JAXBContext context = JAXBContext.newInstance(Module.class, Script.class, TheModule.class);
Writer writer = new StringWriter();
context.createMarshaller().marshal(script, writer);
return writer.toString();
}
private void runTest() throws Exception {
Script script = createScript();
System.out.println(marshal(script));
}
public static void main(String[] args) throws Exception {
new JaxbTest().runTest();
}
}
I receive XML, in Java 6:
<script>
<modules>
<module>
<id>Module1</id>
</module>
<theModule>
<other>Module1</other>
<id>Module2</id>
<feature>featured module</feature>
</theModule>
<module>
<other>Module2</other>
<id>Module3</id>
</module>
</modules>
</script>
Note that reference to the m2 (TheModule instance) is serialized as expected. But when the same code is running under Java 7 (Jaxb 2.2.4-1) I receive:
<script>
<modules>
<module>
<id>Module1</id>
</module>
<theModule>
<other>Module1</other>
<id>Module2</id>
<feature>featured module</feature>
</theModule>
<module>
<other xsi:type="theModule" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<other>Module1</other>
<id>Module2</id>
<feature>featured module</feature>
</other>
<id>Module3</id>
</module>
</modules>
</script>
So you can see that on latest JAXB @XmlIDREF for inherited module is not working!
This answer is wrong. Instead of relying on it read the documentation for JAXBContext.newInstance(...)
, see this answer and the comments below.
I think you confuse JAXB with the following line.
JAXBContext.newInstance(Module.class, Script.class, TheModule.class);
You tell it that you want to serialize the XML types Script
, Module
and TheModule
. JAXB will treat objects of this latter type in a special way, because you've already supplied its base class: it adds a discriminating attribute to it. This way these two types can be differentiated in the serialized XML.
Try supplying only Script
and Module
, the base class for all modules.
JAXBContext.newInstance(Module.class, Script.class);
In fact, you can leave out Module
entirely. JAXB will infer types in the Script
object you try to serialize from the context.
This behaviour by the way isn't exactly related to Java 6. It's related to the JAXB implementation in use (okay-okay, almost the same thing, I know). In my projects I use JAXB 2.2.4-1, which reproduces the issue at hand in Java 6 and 7 too.
Oh, and one more thing: instead of creating a StringWriter
and marshalling objects into that, then sending its contents to System.out
you can use the following to send formatted XML to stdout
.
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(script, System.out);
Maybe this can ease (a tiny little bit) further testing.
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