We are using MOXy JAXB in our project.
Model class:
@XmlRootElement(name = "field")
@XmlType(propOrder = {"id","value"})
public class FieldData{
@XmlAttribute
private String id;
@XmlAttribute
private Object value;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Object getValue() {
return value;
}
public void setValue(Object value) {
this.value = value;
}
}
For my use case, I want value to be of type Object itself as I may get any primitive data type value here. I will take them as strings initially. Once I get the object, I do the type conversion and save it into the same field. The above use case is working fine. But when I change @XmlAttribute to @XmlElement it is not working. I see that the value is unmarshalled as an instance of ElementNSImpl. Is there any work around for this?
Here is a brain dump on what you are seeing:
I will use the same demo code with the different mappings described below:
import java.io.File;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Foo.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();
File xml = new File("input.xml");
Foo foo = (Foo) unmarshaller.unmarshal(xml);
System.out.println(foo.getBar().getClass());
}
}
@XmlAttribute
Foo
We use the @XmlAttribute
annotation to map a property to an @XmlAttribute
. Note: This isn't a valid configuration when using the JAXB reference implementation.
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Foo {
private Object bar;
@XmlAttribute
public Object getBar() {
return bar;
}
public void setBar(Object bar) {
this.bar = bar;
}
}
input.xml
In the XML document below the bar
attribute contains numeric digits.
<?xml version="1.0" encoding="UTF-8"?>
<foo bar="123"/>
output
As there is no typing information (in the XML or in the Java class), MOXy brings the value in as a String
. A String
is the most concrete type that can represent all possible values on an XML attribute.
class java.lang.String
input.xml
In the XML document below the bar
attribute contains alphabet characters.
<?xml version="1.0" encoding="UTF-8"?>
<foo bar="Hello World"/>
output
A String
is the most concrete type that can represent all possible values on an XML attribute.
class java.lang.String
@XmlElement
Foo
In this version of the Foo
class we will not annotate the bar
property, this is the same as annotating it with @XmlElement
.
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Foo {
private Object bar;
public Object getBar() {
return bar;
}
public void setBar(Object bar) {
this.bar = bar;
}
}
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>Hello World</bar>
</foo>
Output
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
input.xml
Instead of containing just test, now the bar
element contains XML attributes and child elements.
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar a="1">
<b>2</b>
<c>3</c>
</bar>
</foo>
Output
Now we start to see why JAXB treats the value as a DOM, the element could be arbitrarily complex so a DOM element becomes a structure that can hold any possible value.
class com.sun.org.apache.xerces.internal.dom.ElementNSImpl
input.xml
In the XML document below
<?xml version="1.0" encoding="UTF-8"?>
<foo xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<bar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xsd:string">Hello World</bar>
</foo>
Output
class java.lang.String
@XmlElement(type=String)
Foo
In this version of the Foo
class we will annotate the bar
property with @XmlElement(type=String.class). As far as Java is concerned the property is still of type
Object, but JAXB will treat the property as if it's type
String`.
import javax.xml.bind.annotation.*;
@XmlRootElement
public class Foo {
private Object bar;
@XmlElement(type=String)
public Object getBar() {
return bar;
}
public void setBar(Object bar) {
this.bar = bar;
}
}
Now we see that the value of the bar
element is treated as a String
.
input.xml
<?xml version="1.0" encoding="UTF-8"?>
<foo>
<bar>Hello World</bar>
</foo>
Output
class java.lang.String