I am trying to serialize an object into XML using Jackson's XmlMapper. I want it to have namespaces in appropriate areas. When I serialize the object, however, every attribute is having a namespace - attributes that are directly part of the class I'm trying to serialize will have an xmlns="", and each attribute on referenced classes contains their own namespace prefix (rather than a single prefix for the whole referenced class).
I'm using Jackson 2.9.8
The main class I want to serialize:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"name",
"objectDescription",
"exampleFieldClass"
})
@XmlRootElement(name = "ExampleClass", namespace = "urn:ExampleClass.dto")
public class ExampleClass
{
@XmlElementRef(name = "name", namespace = "urn:ExampleClass.dto", type = JAXBElement.class, required = false)
protected JAXBElement<String> name;
@XmlElementRef(name = "objectDescription", namespace = "urn:ExampleClass.dto", type = JAXBElement.class, required = false)
protected JAXBElement<String> objectDescription;
@XmlElementRef(name = "exampleFieldClass", namespace = "urn:ExampleFieldClass.dto", type = JAXBElement.class, required = false)
protected JAXBElement<ExampleFieldClass> ExampleFieldClass;
public JAXBElement<String> getName() {
return name;
}
public void setName(JAXBElement<String> name) {
this.name = name;
}
public JAXBElement<String> getObjectDescription() {
return objectDescription;
}
public void setObjectDescription(JAXBElement<String> objectDescription) {
this.objectDescription = objectDescription;
}
public JAXBElement<ExampleFieldClass> getExampleFieldClass() {
return ExampleFieldClass;
}
public void setExampleFieldClass(JAXBElement<ExampleFieldClass> ExampleFieldClass) {
this.ExampleFieldClass = ExampleFieldClass;
}
}
A special type:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "ExampleFieldClass", namespace = "urn:ExampleFieldClass.dto", propOrder = {
"id", "dtoType"
})
public class ExampleFieldClass {
@XmlElement(name = "id", namespace = "urn:ExampleFieldClass.dto")
protected String id;
@XmlElement(name = "dtoType", namespace = "urn:ExampleFieldClass.dto")
protected String dtoType;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getDtoType() {
return dtoType;
}
public void setDtoType(String dtoType) {
this.dtoType = dtoType;
}
}
The XmlMapper config and a test:
public class ExampleTest {
private final ExampleObjectFactory factory = new ExampleObjectFactory();
@Test
public void testSerialize() throws Exception {
XmlMapper mapper = getXmlMapper();
ExampleFieldClass exampleFieldClass = new ExampleFieldClass();
exampleFieldClass.setDtoType("dtoType");
exampleFieldClass.setId("id");
ExampleClass exampleClass = new ExampleClass();
exampleClass.setName(factory.createExampleClassName("name"));
exampleClass.setObjectDescription(factory.createExampleClassObjectDescription("description"));
exampleClass.setExampleFieldClass(factory.createExampleClassExampleFieldClass(exampleFieldClass));
String serialized = mapper.writeValueAsString(exampleClass);
System.out.println(serialized);
}
private XmlMapper getXmlMapper() {
JacksonXmlModule module = new JacksonXmlModule();
module.addSerializer(JAXBElement.class, new JsonSerializer<JAXBElement>() {
@Override
public void serialize(JAXBElement value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value.isNil()) {
gen.writeNull();
} else {
gen.writeObject(value.getValue());
}
}
});
XmlMapper objectMapper = new XmlMapper(module);
objectMapper.registerModule(new JaxbAnnotationModule());
objectMapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
return objectMapper;
}
@XmlRegistry
public static class ExampleObjectFactory {
@XmlElementDecl(namespace = "urn:ExampleClass.dto", name = "name", scope = ExampleClass.class)
public JAXBElement<String> createExampleClassName(String value) {
QName _ExampleClassName_QNAME = new QName("urn:ExampleClass.dto", "name");
return new JAXBElement<>(_ExampleClassName_QNAME, String.class, ExampleClass.class, value);
}
@XmlElementDecl(namespace = "urn:ExampleClass.dto", name = "objectDescription", scope = ExampleClass.class)
public JAXBElement<String> createExampleClassObjectDescription(String value) {
QName _ExampleClassObjectDescription_QNAME = new QName("urn:ExampleClass.dto", "objectDescription");
return new JAXBElement<>(_ExampleClassObjectDescription_QNAME, String.class, ExampleClass.class, value);
}
@XmlElementDecl(namespace = "urn:ExampleFieldClass.dto", name = "ExampleFieldClass", scope = ExampleFieldClass.class)
public JAXBElement<ExampleFieldClass> createExampleClassExampleFieldClass(ExampleFieldClass value) {
QName _ExampleClassExampleFieldClass_QNAME = new QName("urn:ExampleFieldClass.dto", "ExampleFieldClass");
return new JAXBElement<>(_ExampleClassExampleFieldClass_QNAME, ExampleFieldClass.class, ExampleClass.class, value);
}
}
}
The actual XML output is this:
<?xml version='1.0' encoding='UTF-8'?>
<ExampleClass xmlns="urn:ExampleClass.dto">
<name xmlns="">name</name>
<objectDescription xmlns="">description</objectDescription>
<exampleFieldClass xmlns="">
<wstxns1:id xmlns:wstxns1="urn:ExampleFieldClass.dto">id</wstxns1:id>
<wstxns2:dtoType xmlns:wstxns2="urn:ExampleFieldClass.dto">dtoType</wstxns2:dtoType>
</exampleFieldClass>
</ExampleClass>
The namespaces shouldn't be defined on every attribute, nor should there be ones defined as an empty String.
Something to note: the empty namespaces xmlns="" will not be there if I change the attribute annotations to @XmlElement; however, it will no longer handle JaxbElement properly (empty vs null).
Does anyone know what I'm doing wrong for this?