How might I marshall an object hierarchy such that, instead of having the component objects becoming nested XML elements, their properties become direct children of the root element with their names prefixed by their type.
For example, given:
(A)
public class Customer {
protected String firstName;
protected String lastName;
protected Address address;
}
public class Address {
protected String street;
protected String city;
}
Using the usual JAXB annotations would result in
(B)
<customer>
<firstName>Juan</firstName>
<lastName>dela Cruz</lastName>
<address>
<street>123 Rizal Avenue</street>
<city>Manila</city>
</address>
</customer>
But, instead, I need to marshall the same as
(C)
<customer>
<firstName>Juan</firstName>
<lastName>dela Cruz</lastName>
<address_street>123 Rizal Avenue</address_street>
<address_city>Manila</address_city>
</customer>
It would be great if there were some JAXB incantation to solve my needs since I'm already using JAXB for most of the things around this problem. In fact, these present some constraints to my specific situation:
Address
would not contain any other complex type.I'm looking at the @XmlPath
annotation from MOXy, but I can't figure out how to get the node names prefixed as in (C).
I dream of a solution where some xjc customizations providing the right JAXB annotations get me going, but that's looking unlikely from my searches so far. Any non-JAXB solution would suffice.
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
You can use the external mapping file in EclipseLink JAXB (MOXy) to apply a second mapping to your object model, and the @XmlPath
extension to flatten the object model.
External Mapping File (oxm.xml)
The following is what the external mapping file might look like if your model classes where in a package called forum11007814
.
<?xml version="1.0"?>
<xml-bindings
xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm"
package-name="forum11007814"
xml-accessor-type="FIELD">
<java-types>
<java-type name="Customer">
<xml-root-element/>
<java-attributes>
<xml-element java-attribute="address" xml-path="."/>
</java-attributes>
</java-type>
<java-type name="Address">
<java-attributes>
<xml-element java-attribute="street" name="address_street"/>
<xml-element java-attribute="city" name="address_city"/>
</java-attributes>
</java-type>
</java-types>
</xml-bindings>
Demo
Below is code that demonstrates how to leverage MOXy's external mapping file when creating the JAXBContext
.
package forum11007814;
import java.io.File;
import java.util.*;
import javax.xml.bind.*;
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, "forum11007814/oxm.xml");
JAXBContext jc = JAXBContext.newInstance(new Class[] {Customer.class}, properties);
File xml = new File("src/forum11007814/c.xml");
Unmarshaller unmarshaller = jc.createUnmarshaller();
Customer customer = (Customer) unmarshaller.unmarshal(xml);
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(customer, System.out);
}
}
jaxb.properties
To specify MOXy as your JAXB provider you need to add a file called jaxb.properties
in the same package as your domain model with the following entry.
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
c.xml/Output
<?xml version="1.0" encoding="UTF-8"?>
<customer>
<firstName>Juan</firstName>
<lastName>dela Cruz</lastName>
<address_street>123 Rizal Avenue</address_street>
<address_city>Manila</address_city>
</customer>
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