Remove xsi:type, xmlns:xs, and xmlns:xsi from JAXB Generics
Solution 1
Test Generic
Your JAXB (JSR-222) implementation is going to create mappings per class (i.e. TestGeneric
, not type (i.e. TestGeneric<Integer>
). As such it is going to treat the value
field as type Object
. This will result in the xsi:type
attributes as your JAXB implementation is adding enough information to be able to unmarshal the same types.
@XmlAccessorType(XmlAccessType.NONE)
static class TestGeneric<T> {
@XmlAttribute public boolean isRequired;
@XmlElement public T value;
public TestGeneric() {
}
public TestGeneric(boolean isRequired) {
this.isRequired = isRequired;
}
}
Demo
Below is an approach you could use. I have introduced subclasses of TestGeneric
to represent the different possible types.
package forum11192623;
import java.io.PrintWriter;
import java.io.StringWriter;
import javax.xml.bind.*;
import javax.xml.bind.annotation.*;
public class Demo {
public static void main(String[] args) {
try {
TestRoot root = new TestRoot();
root.name.value = "bobby";
root.age.value = 102;
root.color.value = "blue";
JAXBContext context = JAXBContext.newInstance(root.getClass());
Marshaller marsh = context.createMarshaller();
marsh.setProperty(Marshaller.JAXB_ENCODING,"UTF-8");
marsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
marsh.marshal(root,pw);
System.out.println(sw.toString());
}
catch(Throwable t) {
t.printStackTrace();
}
}
@XmlRootElement
static class TestRoot {
@XmlElement public TestString name = new TestString(true);
@XmlElement public TestInteger age = new TestInteger(true);
@XmlElement public TestString color = new TestString(true);
}
@XmlAccessorType(XmlAccessType.NONE)
@XmlTransient
@XmlSeeAlso({TestInteger.class, TestString.class})
static class TestGeneric<T> {
@XmlAttribute
public boolean isRequired;
public T value;
public TestGeneric() {
}
public TestGeneric(boolean isRequired) {
this.isRequired = isRequired;
}
}
static class TestInteger extends TestGeneric<Integer> {
public TestInteger() {
}
public TestInteger(boolean b) {
super(b);
}
@XmlElement
public Integer getValue() {
return value;
}
public void setValue(Integer value) {
this.value = value;
}
}
static class TestString extends TestGeneric<String> {
public TestString() {
}
public TestString(boolean b) {
super(b);
}
@XmlElement
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
}
Solution 2
I was able to use @XmlAnyElement(lax=true) on the property that was causing me problems due to the generic parameter, then had to add an @XmlRootElement on the actual class that was being marshalled, and the declaration went away! Fantastic!
Solution 3
i get the same problem, and resolved it by the advice from Programmatically determine if a JAXB annotation will result in a xsi:type annotation?
just using @XmlAnyElement(lax=true)
instead of using @XmlElement
on the generic class object (value for this sample).
Comments
-
esotericpig almost 2 years
When using JAXB, I'd like to remove the excess namespaces/types from my XML elements when using Generics. How can I do this or what am I doing wrong? I'd like to use Generics so that I only have to write a block of code once.
Example code:
public static void main(String[] args) { try { TestRoot root = new TestRoot(); root.name.value = "bobby"; root.age.value = 102; root.color.value = "blue"; JAXBContext context = JAXBContext.newInstance(root.getClass()); Marshaller marsh = context.createMarshaller(); marsh.setProperty(Marshaller.JAXB_ENCODING,"UTF-8"); marsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE); StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); marsh.marshal(root,pw); System.out.println(sw.toString()); } catch(Throwable t) { t.printStackTrace(); } } @XmlRootElement static class TestRoot { @XmlElement public TestGeneric<String> name = new TestGeneric<String>(true); @XmlElement public TestGeneric<Integer> age = new TestGeneric<Integer>(true); @XmlElement public TestWhatIWouldLike color = new TestWhatIWouldLike(true); } @XmlAccessorType(XmlAccessType.NONE) static class TestGeneric<T> { @XmlAttribute public boolean isRequired; @XmlElement public T value; public TestGeneric() { } public TestGeneric(boolean isRequired) { this.isRequired = isRequired; } } @XmlAccessorType(XmlAccessType.NONE) static class TestWhatIWouldLike { @XmlAttribute public boolean isRequired; @XmlElement public String value; public TestWhatIWouldLike() { } public TestWhatIWouldLike(boolean isRequired) { this.isRequired = isRequired; } }
Output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <testRoot> <name isRequired="true"> <value xsi:type="xs:string" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">bobby</value> </name> <age isRequired="true"> <value xsi:type="xs:int" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">102</value> </age> <color isRequired="true"> <value>blue</value> </color> </testRoot>
-
esotericpig almost 12 yearsThis will work, but I feel like it defeats the purpose of Generics -- which for me is to write & maintain one block of code that will be useable for any Object without having to make new classes. I guess I will have to just deal with having the types specified, unless someone else has any other good ideas?