Java Code to XML/XSD without using Annotation
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
DOMAIN MODEL
I will use the following domain model for this answer. Note how there are no JAXB annotations on the model.
Customer
package forum11693552;import java.util.*;public class Customer { private String firstName; private String lastName; private List<PhoneNumber> phoneNumbers = new ArrayList<PhoneNumber>(); public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public List<PhoneNumber> getPhoneNumbers() { return phoneNumbers; } public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) { this.phoneNumbers = phoneNumbers; }}
PhoneNumber
package forum11693552;public class PhoneNumber { private String type; private String number; public String getType() { return type; } public void setType(String type) { this.type = type; } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; }}
OPTION #1 - Any JAXB (JSR-222) Implementation
JAXB is configurartion by exception, this means you only need to add annotations where you want the mapping behaviour to differ from the default. Below is a link to an example demonstrating how to use any JAXB impl without annotations:
Demo
package forum11693552;import javax.xml.bind.*;import javax.xml.namespace.QName;public class Demo { public static void main(String[] args) throws Exception { JAXBContext jc = JAXBContext.newInstance(Customer.class); Customer customer = new Customer(); customer.setFirstName("Jane"); customer.setLastName("Doe"); PhoneNumber workPhone = new PhoneNumber(); workPhone.setType("work"); workPhone.setNumber("555-1111"); customer.getPhoneNumbers().add(workPhone); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer); marshaller.marshal(rootElement, System.out); }}
Output
<customer> <firstName>Jane</firstName> <lastName>Doe</lastName> <phoneNumbers> <number>555-1111</number> <type>work</type> </phoneNumbers></customer>
For More Information
OPTION #2 - EclipseLink JAXB (MOXy)'s External Mapping Document
If you do want to customize the mappings, then you may be interested in MOXy's external mapping document extension. A sample mapping document looks like the following:
oxm.xml
<?xml version="1.0"?><xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="forum11693552"> <java-types> <java-type name="Customer"> <xml-root-element /> <java-attributes> <xml-element java-attribute="firstName" name="first-name" /> <xml-element java-attribute="lastName" name="last-name" /> <xml-element java-attribute="phoneNumbers" name="phone-number" /> </java-attributes> </java-type> <java-type name="PhoneNumber"> <java-attributes> <xml-attribute java-attribute="type" /> <xml-value java-attribute="number" /> </java-attributes> </java-type> </java-types></xml-bindings>
jaxb.properties
To enable MOXy as your JAXB provider you need to include a file called jaxb.properties
in the same package as your domain model with the following entry (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html):
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Demo
When using EclipseLink MOXy as your JAXB provider (see), you can leverage the external mapping document when you bootstrap your JAXBContext
package forum11693552;import java.util.*;import javax.xml.bind.*;import javax.xml.namespace.QName;import org.eclipse.persistence.jaxb.JAXBContextFactory;public class Demo { public static void main(String[] args) throws Exception { Map<String, Object> properties = new HashMap<String,Object>(1); properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, "forum11693552/oxm.xml"); JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties); Customer customer = new Customer(); customer.setFirstName("Jane"); customer.setLastName("Doe"); PhoneNumber workPhone = new PhoneNumber(); workPhone.setType("work"); workPhone.setNumber("555-1111"); customer.getPhoneNumbers().add(workPhone); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); JAXBElement<Customer> rootElement = new JAXBElement<Customer>(new QName("customer"), Customer.class, customer); marshaller.marshal(rootElement, System.out); }}
Output
<?xml version="1.0" encoding="UTF-8"?><customer> <first-name>Jane</first-name> <last-name>Doe</last-name> <phone-number type="work">555-1111</phone-number></customer>
For More Information
Have you looked at XStream ? It will deserialise/deserialise a standard POJO without annotations or XSDs. You can provide customisations to affect how elements appear in the XML and pretty much works out-of-the-box.
You could write a custom XmlAdapter
and annotate fields of the constrained type with a XmlJavaTypeAdapter
annotation. The basics would be something like this:
public enum CannotBeAnnotated { value1, value2; }@XmlRootElement(name="client")public class ClientClass { @XmlJavaTypeAdapter(Bridge.class) public CannotBeAnnotated;}@XmlRootElement(name="representation")public class XmlType { @XmlValue public String value;}public class Bridge extends XmlAdapter<XmlType, CannotBeAnnotated>{ public XmlType marshal(CannotBeAnnotated c) { XmlType x=new XmlType(); x.value=c.name(); return x; } public CannotBeAnnotated unmarshall(XmlType x) { return CannotBeAnnotated.valueOf(x.value); }}
Of course for enums this would not be useful as JAXB knows how to deal with them. I just picked an enum for simplicity so you can see the idea:
- Design an XML representation that you do control
- Write an adapter converting that Java type into the desired type
- Annotate "client" code referencing the adapter for the desired type
- Profit.