Consider the following code:
Main.java
====
package com.sample;
import com.sample.entity.Customer;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
public class Main {
public static void main(String[] args) throws JAXBException {
JAXBContext jc = JAXBContext.newInstance(Customer.class);
Customer customer = new Customer();
customer.setId(123);
Marshaller m = jc.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
m.marshal(customer, System.out);
}
}
Customer.java
====
package com.sample.entity;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement
public class Customer {
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
package-info.java
====
@XmlSchema(namespace = "http://www.example.org/package", elementFormDefault = XmlNsForm.QUALIFIED)
package com.sample.entity;
import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNsForm;
I wish to reuse the Customer POJO and create two different namespace values. So first I wish to print the output
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns="http://www.example.org/package">
<id>123</id>
</customer>
like the current code does, then create a second xml with a different main namespace from the same pojo that will look like this
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns="http://www.another.org/package">
<id>123</id>
</customer>
or remove the namespace all together
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<id>123</id>
</customer>
This is a good question.
Instead of working on the type-level you could work on the Jaxb node level. There is almost no overhead involved.
The key is creating/accessing JAXBElement
(node level elements) out of your business logic classes and customize the XML-Node it represents with an according QName
definition.
QName
provides several options for customization, ranging from fully qualified namespaces to no namespace at all. The only caveat is that using a namespace without prefix is not possible. Jaxb will use a default namespace if you don't provide one.
Example:
QName elaborateQName = new QName("http://www.another.org/package", "customer", "myNs");
QName simpleQName = new QName("customer");
JAXBElement jx1 = new JAXBElement(elaborateQName, customer.getClass(), customer);
JAXBElement jx2 = new JAXBElement(simpleQName, customer.getClass(), customer);
m.marshal(jx1, System.out);
m.marshal(jx2, System.out);
This will produce the following output:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<myNs:customer xmlns:myNs="http://www.another.org/package">
<id>123</id>
</myNs:customer>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer>
<id>123</id>
</customer>
Using a namespace without prefix is not possible. Trying a QName with empty prefix or the equivalent XMLConstants.DEFAULT_NS_PREFIX
as parameter will result in a default prefix (like ns1) being generated:
// QName defaultNs = new QName("http://www.another.org/package", "customer", "");
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns2:customer xmlns:ns1="http://www.another.org/package">
<id>123</id>
</ns2:customer>
Using null as the parameter for prefix
yields an Exception:
java.lang.IllegalArgumentException: prefix cannot be "null" when creating a QName
at javax.xml.namespace.QName.<init>(QName.java:251)
How to override the root xmlns attribute with MOXy:
EclipseLink MOXy provides easy customization of JAXB marshalling, including changing the default namespace of your root element. The Object to XML mapping (OXM) for overriding the default namespace is:
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" version="2.5">
<xml-schema element-form-default="QUALIFIED" namespace="http://www.another.org/package"/>
</xml-bindings>
To have no default namespace, use <xml-schema element-form-default="UNSET"/>
instead.
MOXy Setup:
1) add the library to your project:
<dependency>
<groupId>org.eclipse.persistence</groupId>
<artifactId>org.eclipse.persistence.moxy</artifactId>
<version>2.6.2</version>
</dependency>
2) Add a jaxb.properties
to your object model package to enable MOXy's factory (e.g. src/main/resources/com/sample/entity/jaxb.properties
):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
3) Instantiate your context according to your OXM configuration (in this example, the OXM file is at src/main/resources/com/sample/entity/my-oxm.xml
):
Map<String, Source> metadata = Collections.singletonMap("com.sample.entity", new StreamSource(Customer.class.getResourceAsStream("my-oxm.xml")));
Map<String, Object> properties = Collections.singletonMap(JAXBContextProperties.OXM_METADATA_SOURCE, metadata);
JAXBContext jaxbContext = JAXBContext.newInstance(new Class[] {customer.getClass()}, properties);
You can then use marshalling on the JAXBContext
as normal. Instantiate separate contexts for each different OXM file you want to use.
Additional customizations with MOXy:
Using MOXy allows you to further customize the marshalling of your entity in the future without changing your object model, effectively making your marshalling logic adhere to the open/closed principle even though you have no explicit Marshaller
classes. For instance, if you needed to marshal your Customer
object as a Person
, you could do this by adding another OXM file:
<?xml version="1.0"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" version="2.5">
<xml-schema element-form-default="QUALIFIED" namespace="http://www.example.com/person"/>
<java-types>
<java-type name="com.sample.entity.Customer">
<xml-root-element name="person"/>
<java-attributes>
<xml-attribute java-attribute="id" name="personId"/>
<xml-element java-attribute="id" xml-path="someOtherId/text()"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
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