Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.
EclipseLink/Development/2.1/DynamicMOXy/296967/Documentation
Dynamic MOXy - JAXB with Dynamically Generated Java Classes
EclipseLink Dynamic MOXy introduces a new concept in JAXB development - the freedom to bootstrap a JAXBContext from a variety of metadata sources and use familiar JAXB APIs to marshal and unmarshal data, all without having actual compiled Java class files on the classpath. This gives the user the flexibility to alter their metadata and not have to worry about updating and recompiling the previously-generated Java source code.
Dynamic Entities
Instead of actual Java classes (e.g. Customer.class, Address.class, etc), the "domain" objects used in Dynamic MOXy will be subclasses of DynamicEntity. DynamicEntities offer a simple get(propertyName) / set(propertyName, propertyValue) API to manipulate their data:
Following is an example of using the DynamicEntity APIs:
DynamicEntity customer = (DynamicEntity) dynamicJAXBContext.createUnmarshaller().unmarshal(instanceDoc); String lastName = customer.get("lastName"); List orders = customer.get("orders"); ... DynamicEntity address = dContext.newDynamicEntity("mynamespace.Address"); address.set("street", "1001 Fleet St."); customer.set("lastName", lastName + "Jr."); customer.set("address", address);
Getting Started
As with conventional JAXB, the first step is to create a JAXBContext. This is achieved by use of the DynamicJAXBContextFactory class. DynamicJAXBContexts cannot be instantiated directly, they must be created through the factory API.
Currently, a DynamicJAXBContext can be created with metadata from an XML Schema file (XSD), or from an EclipseLink Project specified in the EclipseLink sessions.xml file.
Bootstrapping from XML Schema (XSD)
If you have an existing XML Schema that you would like to map to, you can provide that Schema to DynamicJAXBContextFactory to create a DynamicJAXBContext. The schema will be parsed and DynamicTypes will be generated for the complex types contained within it.
There are three ways that you can pass your XML Schema metadata to DynamicJAXBContextFactory - using an InputStream, Node, or Source:
/** * Create a DynamicJAXBContext, using XML Schema as the metadata source. * * @param schemaStream * java.io.InputStream from which to read the XML Schema. * @param resolver * An org.xml.sax.EntityResolver, used to resolve schema imports. Can be null. * @param classLoader * The application's current class loader, which will be used to first lookup * classes to see if they exist before new DynamicTypes are generated. Can be * null, in which case Thread.currentThread().getContextClassLoader() will be used. * @param properties * Map of properties to use when creating a new DynamicJAXBContext. Can be null. * * @return * A new instance of DynamicJAXBContext. * * @throws JAXBException * if an error was encountered while creating the DynamicJAXBContext. */ public static DynamicJAXBContext createContextFromXSD(java.io.InputStream schemaStream, EntityResolver resolver, ClassLoader classLoader, Map<String, ?> properties) throws JAXBException public static DynamicJAXBContext createContextFromXSD(org.w3c.dom.Node schemaDOM, EntityResolver resolver, ClassLoader classLoader, Map<String, ?> properties) throws JAXBException public static DynamicJAXBContext createContextFromXSD(javax.xml.transform.Source schemaSource, EntityResolver resolver, ClassLoader classLoader, Map<String, ?> properties) throws JAXBException
Example
Using the following example schema, we will show an example of how to create and marshall a new object using Dynamic MOXy:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema targetNamespace="myNamespace" xmlns:myns="myNamespace" xmlns:xs="http://www.w3.org/2001/XMLSchema" attributeFormDefault="qualified" elementFormDefault="qualified"> <xs:element name="customer" type="myns:customer"/> <xs:complexType name="customer"> <xs:sequence> <xs:element name="first-name" type="xs:string"/> <xs:element name="last-name" type="xs:string"/> <xs:element name="address" type="myns:address"/> </xs:sequence> </xs:complexType> <xs:complexType name="address"> <xs:sequence> <xs:element name="street" type="xs:string"/> <xs:element name="city" type="xs:string"/> <xs:element name="province" type="xs:string"/> <xs:element name="postal-code" type="xs:string"/> </xs:sequence> </xs:complexType> </xs:schema>
The following code demonstrates:
- Passing the XML Schema to DynamicJAXBContextFactory to create a DynamicJAXBContext
- Creating new DynamicEntities and setting their properties
- Creating a JAXBMarshaller and marshalling the Java objects to XML
InputStream inputStream = ClassLoader.getSystemResourceAsStream("com/foo/sales/xsd/customer.xsd"); DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, null, null, null); DynamicEntity newCustomer = dContext.newDynamicEntity("mynamespace.Customer"); newCustomer.set("firstName", "George"); newCustomer.set("lastName", "Jones"); DynamicEntity newAddress = dContext.newDynamicEntity("mynamespace.Address"); newAddress.set("street", "227 Main St."); newAddress.set("city", "Toronto"); newAddress.set("province", "Ontario"); newAddress.set("postalCode", "M5V1E6"); newCustomer.set("address", address); dContext.createMarshaller().marshal(newCustomer, System.out);
Bootstrapping from an EclipseLink Project
If you would like to have more control over how your DynamicEntities will be mapped to XML, you also have the option of bootstrapping from an EclipseLink Project. Using this approach, you can take advantage of EclipseLink's robust mappings framework and explicitly specify how each complex type in XML maps to its Java counterpart. Please consult the EclipseLink MOXy documentation for information on constructing Projects. (*** TODO: Add Link Here ***)
Once you have your EclipseLink Project, you will need to include it in your EclipseLink sessions.xml file (for more information on sessions.xml please see the EclipseLink documentation). (*** TODO: Add Link Here ***) You can then pass the Session's name to DynamicJAXBContextFactory to create your DynamicJAXBContext using the following API:
/** * Create a <tt>DynamicJAXBContext</tt>, using an EclipseLink <tt>sessions.xml</tt> as the metadata source. * The <tt>sessionNames</tt> parameter is a colon-delimited list of session names within the * <tt>sessions.xml</tt> file. <tt>Descriptors</tt> in this session's <tt>Project</tt> must <i>not</i> * have <tt>javaClass</tt> set, but <i>must</i> have <tt>javaClassName</tt> set. * * @param sessionNames * A colon-delimited <tt>String</tt> specifying the session names from the <tt>sessions.xml</tt> file. * @param classLoader * The application's current class loader, which will be used to first lookup * classes to see if they exist before new <tt>DynamicTypes</tt> are generated. Can be * <tt>null</tt>, in which case <tt>Thread.currentThread().getContextClassLoader()</tt> will be used. * @param properties * Map of properties to use when creating a new <tt>DynamicJAXBContext</tt>. Can be null. * * @return * A new instance of <tt>DynamicJAXBContext</tt>. * * @throws JAXBException * if an error was encountered while creating the <tt>DynamicJAXBContext</tt>. */ public static DynamicJAXBContext createContext(String sessionNames, ClassLoader classLoader, Map<String, ?> properties) throws JAXBException
Example
Here is an example of an EclipseLink MOXy Project in code. Notice that we have customizations that are made:
- We have specified that the package name of the generated classes should be com.mypackage. If importing from XSD, we would have used the default namespace to build the package name instead, which would result in a package name of mynamespace.
- We have specified that the first-name element in XML should be mapped to a field called fName in Java. If importing from XSD we would have generated a field called firstName.
- We have specified that a null address will be represented by xsi:nil="true" in XML.
package com.mypackage; import org.eclipse.persistence.oxm.NamespaceResolver; import org.eclipse.persistence.oxm.XMLConstants; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.mappings.XMLDirectMapping; import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping; public class CustomerProject extends org.eclipse.persistence.sessions.Project { public CustomerProject() { super(); NamespaceResolver nsResolver = new NamespaceResolver(); nsResolver.put("ns0", "myNamespace"); nsResolver.put("xsi", XMLConstants.SCHEMA_INSTANCE_URL); XMLDescriptor customerDescriptor = new XMLDescriptor(); customerDescriptor.setJavaClassName("com.mypackage.Customer"); customerDescriptor.setDefaultRootElement("customer"); customerDescriptor.setNamespaceResolver(nsResolver); XMLDirectMapping firstNameMapping = new XMLDirectMapping(); firstNameMapping.setAttributeName("fName"); firstNameMapping.setXPath("first-name/text()"); customerDescriptor.addMapping(firstNameMapping); // ... XMLCompositeObjectMapping addressMapping = new XMLCompositeObjectMapping(); addressMapping.setAttributeName("address"); addressMapping.setXPath("address"); addressMapping.setReferenceClassName("com.mypackage.Address"); addressMapping.getNullPolicy().setNullRepresentedByXsiNil(true); addressMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.XSI_NIL); customerDescriptor.addMapping(addressMapping); XMLDescriptor addressDescriptor = new XMLDescriptor(); addressDescriptor.setJavaClassName("com.mypackage.Address"); // ... this.addDescriptor(customerDescriptor); this.addDescriptor(addressDescriptor); } }
Next, here is an example sessions.xml that includes our Project:
<?xml version="1.0" encoding="US-ASCII"?> <sessions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ...> <session xsi:type="database-session"> <name>MyCustomerProject</name> <primary-project xsi:type="class">com.mypackage.CustomerProject</primary-project> </session> </sessions>
Finally, we can now instantiate a DynamicJAXBContext using the session name, and begin working with our DynamicEntities:
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContext("MyCustomerProject", null, null); DynamicEntity newCustomer = dContext.newDynamicEntity("com.mypackage.Customer"); newCustomer.set("fName", "Bob"); ... dContext.createMarshaller().marshal(newCustomer, System.out);
Advanced Topics
Dynamic MOXy provides further functionality for more advanced use cases.
Customizing the Generated Mappings with eclipselink-oxm.xml
Bootstrapping from jaxb.properties
Importing other Schemas / EntityResolvers
If the XML Schema that you wish to use to bootstrap contains imports of other schemas, then you must configure an org.xml.sax.EntityResolver that will resolve the locations of the imported schemas, and pass the EntityResolver to DynamicJAXBContextFactory.
Taking our Customer / Address example, let's say that we wish to define these two types in their own schemas: