In JAXB (JSR-222) metadata is applied to Java classes via standard annotations. There are times when using annotatations is not practical, and an alternate mechanism is required. In this post I'll demonstrate how EclipseLink JAXB (MOXy) can leverage XML to represent the JAXB metadata.
An XML metadata representation is useful when:
- You cannot modify the domain model (it may come from a 3rd party).
- You do not want to introduce compile dependencies on JAXB APIs (if you are using a version of Java prior to Java SE 6).
- You want to apply multiple JAXB mappings to a domain model (you are limited to one representation with annotations).
- Your object model already contains so many annotations from other technologies that adding more would make the class unreadable.
Mapping File (binding.xml)
The mapping file contains the same information as the JAXB annotations. Like with annotations you only need to specify metadata to override default behavior.
The mapping file contains the same information as the JAXB annotations. Like with annotations you only need to specify metadata to override default behavior.
<?xml version="1.0"?> <xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="blog.bindingfile"> <xml-schema namespace="http://www.example.com/customer" element-form-default="QUALIFIED"/> <java-types> <java-type name="Customer"> <xml-root-element/> <xml-type prop-order="firstName lastName address phoneNumbers"/> <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>
Domain Model
The following domain model will be used in this example. Because the JAXB metadata is represented as XML, no annotations are used on the classes.
Customer
package blog.bindingfile; import java.util.List; public class Customer { private String firstName; private String lastName; private Address address; private List<PhoneNumber> phoneNumbers; 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 Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } public List<PhoneNumber> getPhoneNumbers() { return phoneNumbers; } public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) { this.phoneNumbers = phoneNumbers; } }
Address
package blog.bindingfile; public class Address { private String street; public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } }
PhoneNumber
package blog.bindingfile; 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; } }
XML (input.xml)
The following XML will be used in this example:
<?xml version="1.0" encoding="UTF-8"?> <customer xmlns="http://www.example.com/customer"> <first-name>Jane</first-name> <last-name>Doe</last-name> <address> <street>123 A Street</street> </address> <phone-number type="work">555-1111</phone-number> <phone-number type="cell">555-2222</phone-number> </customer>
Demo Code
The XML mapping file is passed in via the properties parameter when the JAXBContext is instantiated.
package blog.bindingfile; 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, "blog/bindingfile/binding.xml"); JAXBContext jc = JAXBContext.newInstance("blog.bindingfile", Customer.class.getClassLoader() , properties); Unmarshaller unmarshaller = jc.createUnmarshaller(); Customer customer = (Customer) unmarshaller.unmarshal(new File("src/blog/bindingfile/input.xml")); Marshaller marshaller = jc.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); marshaller.marshal(customer, System.out); } }
Specifying the MOXy JAXB Implementation (jaxb.properties)
In order to use this extension, you must use MOXy as your JAXB implementation. To enable MOXy simply add a file named jaxb.properties in the same package as your domain classes with the following entry:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Further Reading
If you enjoyed this post you may also be interested in:
Very helpful post! I've got it working. Do you have an example of how to plug this into a web-service stack? I have a SOAP-based web service implemented as an EJB3 seam component deployed on JBoss. Where would I put the above code to get the marshalling/unmarshalling of the request to my object model so that it uses the external mappings file?
ReplyDeleteHi Frances,
ReplyDeleteMost JAX-WS implementations are dependent on a particular JAXB implementation. If they do not support plugging in alternate JAXB implementations then you will need to create a service that interacts directly with XML.
If you are using RESTful services via JAX-RS, there is a ContextResolver mechanism that makes specifying the JAXBContext very easy.
-Blaise
Thanks for the quick reply Blaise! Do you know which JAX-WS implementations support EclipseLink MOXy?
ReplyDeleteEclipseLink JAXB (MOXy) is used by the JAX-WS implementation in WebLogic as of version 12.1.1 (see: EclipseLink MOXy is the JAXB Provider in WebLogic Server 12c). MOXy can also be used with the JAX-WS implementation in GlassFish as of version 3.1.2 (see: GlassFish 3.1.2 is Full of MOXy (EclipseLink JAXB)).
DeleteHi Frances,
ReplyDeleteWe are currently working with some JAX-WS implementations. I will post updates when they are available.
-Blaise
Is there a way to generate the binding.xml in Moxy or other implementations of JAXB?
ReplyDeleteHi,
ReplyDeleteCan i use MOXy in my WebService project (i am using JAXWS2.2.1-20100617) to overcome the inheritance issue stated at http://stackoverflow.com/questions/4661263/jaxb-2-x-how-to-override-an-xmlelement-annotation-from-parent-class-mission-i
.
I have the same problem just like bzero.MOXy works perfectly in a standalone application.But i think i'can make it work with my WebService Application ?
Hi elcinsumerkan,
ReplyDeleteWe are currently working with a couple of JAX-WS implementations to provide support for using EclipseLink MOXy as the binding layer. I will post the information on my blog when these implementatations are ready to release.
For now you can use EclipseLink MOXy to handle the XML if you have a Provider based Web Service. I will try to blog about this topic soon.
-Blaise
I am using the CXF Stack on JBoss EAP 5.1.
ReplyDeleteI have the eclipselink.jar in the classpath and the jaxb.properties in the package.
@XmlCDATA is not working for me. Im not sure why so I created the eclipselink-oxm.xml file.
Where do I put that xml file with the bindings in it so that the JAX-WS generated services use it when Marshalling the Models to XML? Thank you.
Hello David,
ReplyDeleteIf you are using JAX-RS with the CXF Stack then the following post should help:
- MOXy's XML Metadata in a JAX-RS Service
-Blaise
Hi David,
ReplyDeleteNot all JAXB implementations support plugging in alternate JAXB implementations. I'm working with a couple now to add support for MOXy, but unfortunately JBoss' is not one of them.
Ideally JAX-WS will be enhanced to offer the same type of pluggability offered by JAX-RS:
- MOXy's XML Metadata in a JAX-RS Service
-Blaise
HI Blaise:
ReplyDeleteIs there a way to auto generate bindings.xml or plugin available in eclipse?
There currently isn't a way to auto generate a bingings.xml. Were you thinking of having one generated from an XML schema instead of having annotations on the classes?
ReplyDelete-Blaise
Thanks for reply Blaise. I have xsd and want to use 'externalized meta data' to marshall / unmarshal.
ReplyDeleteWas just curious if bindings can be auto generated and a developer can edit it to customize it to the needs. Seems manual way is the way to go now..no problem.
regards and many thanks.
Hi, Blaise,
ReplyDeleteIs it possible to get rid of namespace "xmlns" in marshalled xml? I just want to have a "pure" xml generated. Thanks,
Foster
Hi Foster,
ReplyDeleteYour JAXB implementation will only write an "xmlns" declaration if you have included namespace information in your mapping. Below is an example without any namespace declarations:
- http://wiki.eclipse.org/EclipseLink/Examples/MOXy/GettingStarted/TheBasics
The following article may help you determine where you may be specifying namespace information:
- JAXB & Namespaces
-Blaise
Thanks Blaise. I'm actually using "xml-bindings.xml" for marshaling my objects, and it's always give me the xmlns, which I don't want at all - any advise? Thanks,
ReplyDeleteFoster
Hi Foster,
ReplyDeleteIn this example if your remove the <xml-schema> tag from the bindings file, then the marshalled XML document will not be namespace qualified.
-Blaise
I did, but still get:
ReplyDeletexmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
Do you have inheritance in your object model? Also which version of EclipseLink are you using?
ReplyDelete-Blaise
No, I don't have any inheritance, and use EclipseLink 2.3.0.
ReplyDeleteCould you provide me more details about your model via my Contact Me page, so that I can better recreate your setup regarding the xsi namespace declaration?
ReplyDelete-Blaise
Hi Frances,
ReplyDeleteIt took longer than I thought it would, but EclipseLink JAXB (MOXy) is now the default JAXB implementation in WebLogic 12c, and can be used to create JAX-WS web services:
- EclipseLink MOXy is the JAXB Provider in WebLogic Server 12c
-Blaise
That's great news! Nice work! Looking forward to trying it out. I know you mentioned JBoss-AS wasn't in your plans but has that since changed?
ReplyDeleteHi Frances,
DeleteNo news on JBoss and JAX-WS support for MOXy. I am happy to announce that GlassFish 3.1.2 will support MOXy as a JAXB provider in its JAX-WS implementation. I will post an example soo.
-Blaise
Hi,
ReplyDeleteExamples from this post seem not to work at all with the latest 2.3.3-SNAPSHOT.
First, there is the exception: "The property or field lastName was specified in propOrder but is not a valid property".
Then after removing the prop-order definition this is the output:
[EL Warning]: 2012-04-13 15:24:58.39--Ignoring attribute [firstName] on class [mpol.Customer] as no Property was generated for it.
[EL Warning]: 2012-04-13 15:24:58.398--Ignoring attribute [lastName] on class [mpol.Customer] as no Property was generated for it.
[EL Warning]: 2012-04-13 15:24:58.399--Ignoring attribute [phoneNumbers] on class [mpol.Customer] as no Property was generated for it.
[EL Warning]: 2012-04-13 15:24:58.399--Ignoring attribute [type] on class [mpol.PhoneNumber] as no Property was generated for it.
[EL Warning]: 2012-04-13 15:24:58.399--Ignoring attribute [number] on class [mpol.PhoneNumber] as no Property was generated for it.
<?xml version="1.0" encoding="UTF-8"?>
<customer xmlns="http://www.example.com/customer"/>
Hi,
DeleteThis example requires that your domain model include the accessor methods (get/set methods). I originally omitted them from the post to save space, but have just added them back in. I apologize for any confusion.
-Blaise
I see. But then this is a very surprising feature, given that when using annotations they can be added on fields without creating getters/setters.
ReplyDeleteHi,
DeleteYou specify in the mapping file that field access should be used by specifying xml-accessor-type="FIELD" as an attribute on the java-type element.
-Blaise
But xml-accessor-type="FIELD" sets the default to map all fields, like @XmlAccessorType(FIELD).
DeleteWhat you can do with annotations, and apparently not with XML, is to a map a specific field that has no getter/setter, without changing the default accessor type.
One would hope that the XML metadata is able to work in more or less every case that can be mapped with annotations. But it just seems not to be the case here.
This is an issue we are going to address. You can track our progress on this issue with the following bug (which I believe you entered):
Delete- http://bugs.eclipse.org/376831
-Blaise
Can we convert jibx bindings in xml in to the json binding(xslt)
ReplyDeleteThat should be possible. Are you volunteering?
Delete-Blaise
What is the correct location to place the jaxb.properties file? I'm seeing: javax.xml.bind.JAXBException: "com.x.y.z" doesnt contain ObjectFactory.class or jaxb.index
ReplyDeleteI have the file in the com.x.y.z folder and it works in local Eclipse/Tomcat/maven setup but when I deploy to a server, I see the above exception. The code that causes this is:
jc = JAXBContext.newInstance("com.x.y.z", ZMappingController.class.getClassLoader(), properties);
The ZMappingController.java is in a different package (com.x.y.a). Also, is it possible to place the properties file in a common folder like /src/main/resources/
The following example should help:
Delete- https://github.com/bdoughan/blog20120418
-Blaise
Thanks for your quick reply!. It works. I was trying to the same as I was waiting for your reply.
ReplyDeleteHi Blaise..
ReplyDeleteOne quick question
Regarding your first point
1) You cannot modify the domain model (it may come from a 3rd party).
If it is coming from thirdparty, how can they already be equipped with ObjectFactory.class or jaxb.index or jaxb.properties file?
Hi Blaise.
ReplyDeleteE.g.
--------------------------------------------------------------
// Model:
public class Document {
private String name;
private Date date;
getters and setters ...
}
Input XML file:
Some name
23.11.2012
Using annotations it would be:
@XmlPath("document/foo/name/text()")
private String name;
@XmlPath("document/bar/date/text()")
@XmlJavaTypeAdapter(SomeAdapter.class)
private Date date;
--------------------------------------------------------------
Could you please show (or provide link) how to represent @XmlPath and @XmlJavaTypeAdapter in xml binding. I need it for unmarshalling.
Thanks in advance.
Check out example Example 2-11 in the MOXy User Guide for an example of how to map this use case:
Delete- http://www.eclipse.org/eclipselink/documentation/2.4/moxy/runtime003.htm#CACHCHAE
-Blaise
Thanks.
DeleteI've checked the official docs, now everything works without annotations as well ))
MOXy is so nice, thank you for your work!
Hi,
ReplyDeleteIs there a way I can map the same object to different JSON/XML response without having the need to create a different objects.
Definitely. This is one of our primary use cases. Check out the following example where a single object model is mapped to both the Google and Yahoo weather services:
Delete- Mapping Objects to Multiple XML Schemas - Weather Example