Dynamic JAXB/MOXy allows you to bootstrap a JAXBContext
from a variety of metadata sources and use familiar JAXB APIs to marshal and unmarshal data, without requiring compiled domain classes. This is an enhancement over static JAXB, because now you can update the metadata without having to update and recompile the previously-generated Java source code.
The benefits of using dynamic JAXB/MOXy entities are:
Instead of using actual Java classes (for example, Customer.class
, Address.class
, and so on), the domain objects are subclasses of the DynamicEntity
.
Dynamic entities offer a simple get(propertyName)
/set(propertyName propertyValue)
API to manipulate their data.
Dynamic entities have an associated DynamicType
, which is generated in-memory, when the metadata is parsed.
The following Tasks demonstrate how to use dynamic JAXB:
This example demonstrates how to bootstrap a dynamic JAXBContext
from an XML Schema.
Use the DynamicJAXBContextFactory
to create a dynamic JAXBContext
. Example 15-26 to bootstrap a DynamicJAXBContext
from the customer.xsd
schema (Example 15-27) by using createContextFromXSD()
.
Example 15-26 Specifying the Input Stream and Creating the DynamicJAXBContext
import java.io.FileInputStream; import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContext; import org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory; public class Demo { public static void main(String[] args) throws Exception { FileInputStream xsdInputStream = new FileInputStream("src/example/customer.xsd"); DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromXSD(xsdInputStream, null, null, null);
The first parameter represents the XML schema itself and must be in one of the following forms: java.io.InputStream
, org.w3c.dom.Node
, or javax.xml.transform.Source
.
Example 15-27 shows the customer.xsd
schema that represents the metadata for the dynamic JAXBContext you are bootstrapping.
Example 15-27 Sample XML Schema Document
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org" targetNamespace="http://www.example.org" elementFormDefault="qualified"> <xsd:complexType name="address"> <xsd:sequence> <xsd:element name="street" type="xsd:string" minOccurs="0"/> <xsd:element name="city" type="xsd:string" minOccurs="0"/> </xsd:sequence> </xsd:complexType> <xsd:element name="customer"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string" minOccurs="0"/> <xsd:element name="address" type="address" minOccurs="0"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>
To bootstrap DynamicJAXBContext
from an XML schema that contains imports of other schemas, you need to configure an org.xml.sax.EntityResolver
to resolve the locations of the imported schemas and pass the EntityResolver
to DynamicJAXBContextFactory
.
The following example shows two schema documents, customer.xsd
(Example 15-28) and address.xsd
Example 15-29). You can see that customer.xsd
imports address.xsd
by using the statement:
<xsd:import namespace="http://www.example.org/address" schemaLocation="address.xsd"/>
Example 15-28 customer.xsd
<?xml version="1.0" encoding="UTF-8"?> xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:add="http://www.example.org/address" xmlns="http://www.example.org/customer" targetNamespace="http://www.example.org/customer" elementFormDefault="qualified"> <xsd:import namespace="http://www.example.org/address" schemaLocation="address.xsd"/> <xsd:element name="customer"> <xsd:complexType> <xsd:sequence> <xsd:element name="name" type="xsd:string" minOccurs="0"/> <xsd:element name="address" type="add:address" minOccurs="0"/> </xsd:sequence> </xsd:complexType> </xsd:element> </xsd:schema>
Example 15-29 address.xsd
<?xml version="1.0" encoding="UTF-8"?> xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.example.org/address" targetNamespace="http://www.example.org/address" elementFormDefault="qualified"> <xsd:complexType name="address"> <xs:sequence> <xs:element name="street" type="xs:string"/> <xs:element name="city" type="xs:string"/> </xs:sequence> </xsd:complexType> </xsd:schema>
If you want to bootstrap DynamicJAXBContext
from the customer.xsd
schema, you need to pass an entity resolver. Do the following:
To resolve the locations of the imported schemas, you need to implement an entityResolver
by supplying the code shown in Example 15-30.
Example 15-30 Implementing an EntityResolver
class MyEntityResolver implements EntityResolver { public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { // Imported schemas are located in ext\appdata\xsd\ // Grab only the filename part from the full path String filename = new File(systemId).getName(); // Now prepend the correct path String correctedId = "ext/appdata/xsd/" + filename; InputSource is = new InputSource(ClassLoader.getSystemResourceAsStream(correctedId)); is.setSystemId(correctedId); return is; } }
After you implement your DynamicJAXBContext
, pass the EntityResolver
, as shown in Example 15-31.
You might see the following exception when importing another schema:
Internal Exception: org.xml.sax.SAXParseException: schema_reference.4: Failed to read schemadocument '<imported-schema-name>', because 1) could not find the document; 2) the document couldnot be read; 3) the root element of the document is not <xsd:schema>.
To work around this exception, disable XJC's schema correctness check by setting the noCorrectnessCheck
Java property. You can set this property one of two ways:
From within the code, by adding this line:
System.setProperty("com.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck", "true")
From the command line, by using this command:
-Dcom.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck=true
Use your application's current class loader as the classLoader
parameter. This parameter verifies that specified classes exist before new DynamicTypes
are generated. In most cases you can pass null
for this parameter and use Thread.currentThread().getContextClassLoader()
instead.
This example shows how to create dynamic entities and marshal then to XML.
Use the DynamicJAXBContext
to create instances of DynamicEntity
. The entity and property names correspond to the class and property names—in this case, the customer
and address
—that would have been generated if you had used static JAXB.
Example 15-32 Creating the Dynamic Entity
DynamicEntity customer = jaxbContext.newDynamicEntity("org.example.Customer"); customer.set("name", "Jane Doe"); DynamicEntity address = jaxbContext.newDynamicEntity("org.example.Address"); address.set("street", "1 Any Street").set("city", "Any Town"); customer.set("address", address);
The marshaller obtained from the DynamicJAXBContext
is a standard marshaller and can be used normally to marshal instances of DynamicEntity.
Example 15-33 Standard Dynamic JAXB Marshaller
Marshaller marshaller = jaxbContext.createMarshaller(); marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);marshaller.marshal(customer, System.out);
Example 15-34 shows the resultant XML document:
In this example shows how to unmarshal from XML the dynamic entities you created in Task 2: Create Dynamic Entities and Marshal Them to XML. The XML in reference is shown in Example 15-34.
The Unmarshaller obtained from the DynamicJAXBContext
is a standard unmarshaller, and can be used normally to unmarshal instances of DynamicEntity
.
Next, specify which data in the dynamic entity to obtain. Specify this value by using System.out.println()
and passing in the entity name. DynamicEntity
offers property-based data access; for example, get("name")
instead of getName()
:
System.out.println(customer.<String>get("name"));
Instances of DynamicEntity
have a corresponding DynamicType
, which you can use to introspect the DynamicEntity
, as shown in Example 15-36.