'JAXB: Marshalling changes object type to String for XmlIDREF
I am facing a problem when serializing an object. The object is a representation of a stack. The Stack can either consist of individual Layers or a sequence of Layers stored in a LayerSequence. So, Layer and LayerSequence implement Stackable and the Stack holds a List of Stackable. Since Layer and LayerSequence are stored independently of Stack, I use XmlIDREF to reference them by a name.
To help JAXB resolving the interface implementations, I define
@XmlElementWrapper(name="layers")
@XmlElements({
@XmlElement(name="layer", type=Layer.class)
,@XmlElement(name="layerSequence", type=LayerSequence.class)
})
@XmlIDREF
private List<S> layers;
When serializing with a generic Stack<? extends Stackable> the type is not transferred as I would have expected. The type is changed to a String containing the XmlID. The result is:
<stack name="stack">
<layers>
<layer xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xsi:type="xs:string">layer</layer>
</layers>
</stack>
For a specific example, where I know, that only Layers and no LayerSequences are used and I define
@XmlElementWrapper(name="layers")
@XmlElements({
@XmlElement(name="layer", type=Layer.class)
})
@XmlIDREF
private List<S> layers;
the type is properly defined as expected:
<stack name="stack">
<layers>
<layer>layer</layer>
</layers>
</stack>
The same works the other way around for LayerSequence.
What am I missing here? Is this a problem because LayerSequence itself uses Layers? How can I circumvent this?
MWE
Code
import java.util.ArrayList;
import java.util.List;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSeeAlso;
import javax.xml.bind.annotation.XmlTransient;
public class Main
{
public static void main(String[] args) throws PropertyException, JAXBException{
LayerValueImpl layerValue1 = new LayerValueImpl("layerValue");
Layer<LayerValueImpl> layer = new Layer<>("layer",layerValue1);
List<Layer<? extends AbstractLayerValue>> layers = new ArrayList<>();
layers.add(layer);
Stack<? extends Stackable> stack = new Stack<>("stack",layers);
//
JAXBContext jc = JAXBContext.newInstance(Stack.class);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(stack, System.out);
}
public static interface Identifiable<
T extends Identifiable
>
{
public void setName(String name);
public String getName();
}
public static interface Stackable<
T extends Stackable
>
extends
Identifiable<T>
{
@XmlAttribute
@XmlID
@Override
public String getName();
}
@XmlTransient
@XmlAccessorType(XmlAccessType.FIELD)
public static abstract class AbstractLayerValue<
T extends AbstractLayerValue
>
implements
Identifiable<T>
{
@XmlAttribute
@XmlID
private String name;
public AbstractLayerValue() {}
public AbstractLayerValue(
String name
) {
this.name = name;
}
@Override
public void setName(String v){this.name = v;}
@Override
public String getName(){return name;}
}
@XmlRootElement
public static class LayerValueImpl
extends
AbstractLayerValue<
LayerValueImpl
>
{
public LayerValueImpl() {}
public LayerValueImpl(String name) {
super(name);
}
}
@XmlTransient
@XmlAccessorType(XmlAccessType.FIELD)
public static abstract class AbstractLayerSuperClass<
T extends AbstractLayerSuperClass
>
implements
Identifiable<T>
{
@XmlAttribute
@XmlID
private String name;
public AbstractLayerSuperClass() {}
public AbstractLayerSuperClass(
String name
) {
this.name = name;
}
@Override
public void setName(String v){this.name = v;}
@Override
public String getName(){return name;}
}
@XmlRootElement
@XmlSeeAlso({
LayerValueImpl.class
})
public static class Layer<
V extends AbstractLayerValue
>
extends
AbstractLayerSuperClass<
Layer<V>
>
implements
Stackable<Layer<V>>
{
private V value;
public Layer() {}
public Layer(
String name
,V value
) {
super(
name
);
this.value = value;
}
public void setValue(V v){this.value = v;}
public V getValue(){return value;}
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class LayerSequence
implements
Stackable<LayerSequence>
{
@XmlAttribute
@XmlID
private String name;
private List<Layer<? extends AbstractLayerValue>> layers;
public LayerSequence() {}
public LayerSequence(
String name
,List<Layer<? extends AbstractLayerValue>> layers
) {
this.name = name;
this.layers = layers;
}
@Override
public void setName(String v){this.name = v;}
@Override
public String getName(){return name;}
public void setLayers(List<Layer<? extends AbstractLayerValue>> v){this.layers = v;}
public List<Layer<? extends AbstractLayerValue>> getLayers(){return layers;}
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Stack<
S extends Stackable
>
implements
Identifiable<Stack<S>>
{
@XmlAttribute
@XmlID
private String name;
@XmlElementWrapper(name="layers")
@XmlElements({
@XmlElement(name="layer", type=Layer.class)
,@XmlElement(name="layerSequence", type=LayerSequence.class)
})
//@XmlElementRefs({
// @XmlElementRef(type=Layer.class, name="layer"),
// @XmlElementRef(type=LayerSequence.class, name="layerSequence")
//})
@XmlIDREF
private List<S> layers;
public Stack() {}
public Stack(
String name
,List<S> layers
) {
this.name = name;
this.layers = layers;
}
@Override
public void setName(String v){this.name = v;}
@Override
public String getName(){return name;}
public void setLayers(List<S> v){this.layers = v;}
public List<S> getLayers(){return layers;}
}
}
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.mr</groupId>
<artifactId>test</artifactId>
<version>0.0.1</version>
<packaging>jar</packaging>
<!-- PROPERTIES -->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<!-- System -->
<java.version>16</java.version>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<maven.plugin.compiler.version>3.8.1</maven.plugin.compiler.version>
<maven.plugin.enforcer.version>3.0.0-M2</maven.plugin.enforcer.version>
<maven.plugin.jar.version>3.2.0</maven.plugin.jar.version>
<maven.plugin.release.version>2.5.3</maven.plugin.release.version>
<!-- Log4j2 -->
<log4j.api.version>2.13.1</log4j.api.version>
<log4j.core.version>2.13.1</log4j.core.version>
<!-- Lombok -->
<lombok.version>1.18.22</lombok.version>
<maven.plugin.lombok.version>1.18.20.0</maven.plugin.lombok.version>
<!-- Stuff for JAXB -->
<javax.xml.bind.jaxb-api.version>2.4.0-b180830.0359</javax.xml.bind.jaxb-api.version>
<javax.activation.activation.version>1.1</javax.activation.activation.version>
<org.glassfish.jaxb.jaxb-runtime.version>2.3.0-b170127.1453</org.glassfish.jaxb.jaxb-runtime.version>
<maven.plugin.jaxb2.version>2.5.0</maven.plugin.jaxb2.version>
<maven.plugin.jaxb2.locale>en</maven.plugin.jaxb2.locale>
<maven.plugin.jaxb2.clearOutputDir>true</maven.plugin.jaxb2.clearOutputDir>
<maven.plugin.jaxb2.createJavaDocAnnotations>false</maven.plugin.jaxb2.createJavaDocAnnotations>
</properties>
<!-- DEPENDENCIES -->
<dependencies>
<!-- -->
<!-- 3rd party -->
<!-- -->
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- Log4j2 -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>${log4j.api.version}</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>${log4j.core.version}</version>
</dependency>
<!-- Stuff for JAXB -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>${javax.xml.bind.jaxb-api.version}</version>
</dependency>
<dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>${javax.activation.activation.version}</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>${org.glassfish.jaxb.jaxb-runtime.version}</version>
</dependency>
</dependencies>
<!-- BUILD -->
<build>
<!-- PLUGINS -->
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.plugin.compiler.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>${maven.plugin.enforcer.version}</version>
<executions>
<execution>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
<configuration>
<rules>
<requireJavaVersion>
<version>${java.version}</version>
</requireJavaVersion>
</rules>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>${maven.plugin.release.version}</version>
<configuration>
<localCheckout>true</localCheckout>
<pushChanges>false</pushChanges>
</configuration>
</plugin>
<!-- Delombok -->
<plugin>
<groupId>org.projectlombok</groupId>
<artifactId>lombok-maven-plugin</artifactId>
<version>${maven.plugin.lombok.version}</version>
<executions>
<execution>
<phase>generate-sources</phase>
<goals>
<goal>delombok</goal>
</goals>
</execution>
</executions>
<configuration>
<encoding>UTF-8</encoding>
<addOutputDirectory>false</addOutputDirectory>
<sourceDirectory>src/main/java</sourceDirectory>
<withoutUnicodeEscape>true</withoutUnicodeEscape>
</configuration>
</plugin>
<!-- Generate a XSD schema -->
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>${maven.plugin.jaxb2.version}</version>
<executions>
<execution>
<id>schemagen</id>
<goals>
<goal>schemagen</goal>
</goals>
</execution>
</executions>
<configuration>
<sources>
<source>src/main/java/de/mr/test/jaxbschemagenlombok</source>
</sources>
<outputDirectory>${project.build.directory}/generated-sources/schemas</outputDirectory>
<clearOutputDir>${maven.plugin.jaxb2.clearOutputDir}</clearOutputDir>
<createJavaDocAnnotations>${maven.plugin.jaxb2.createJavaDocAnnotations}</createJavaDocAnnotations>
<locale>${maven.plugin.jaxb2.locale}</locale>
</configuration>
</plugin>
</plugins>
<!-- THIS IS THE FINAL NAME OF THE JAR -->
<finalName>${project.artifactId}-${project.version}</finalName>
</build>
</project>
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|
