Jump to: navigation, search

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. DynamicEntities have an associated DynamicType, which will be generated in memory when the metadata is parsed.

Idea.png
DynamicTypes are analogous to Java Classes, whereas DynamicEntities can be thought of as instances of a DynamicType.


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);
Idea.png
XML names found in the metadata (complex type names, element names, attribute names) will be translated to Java identifiers according to the algorithms described in "Appendix D: Binding XML Names to Java Identifiers" of the Java Architecture for XML Binding (JAXB) 2.2 Specification. In the example above, "last-name" in XML was translated to "lastName" for the Java object.




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.

A DynamicJAXBContext can be created from an XML Schema file (XSD), EclipseLink OXM metadata file, 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. EclipseLink MOXy uses Sun's XJC (XML-to-Java Compiler) APIs to parse the schema into an in-memory representation, and then generates DynamicTypes and Mappings.

Idea.png
When bootstrapping from XSD, you will need to include jaxb-xjc.jar (from the JAXB reference implementation) on your CLASSPATH.


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
Idea.png
The classLoader parameter is your application's current class loader, and will be used to first lookup classes to see if they exist before new DynamicTypes are generated. The user may pass in null for this parameter, and Thread.currentThread().getContextClassLoader() will be used instead.


Idea.png
The properties parameter can be used to pass additional properties to DynamicJAXBContextFactory. For more information on these properties see Advanced Topics.




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 = myClassLoader.getSystemResourceAsStream("mynamespace/resources/xsd/customer.xsd");
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, null, myClassLoader, 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", newAddress);
 
dContext.createMarshaller().marshal(newCustomer, System.out);



Bootstrapping from EclipseLink OXM Metadata

If you would like to have more control over how your DynamicEntities will be mapped to XML, you can instead bootstrap from an EclipseLink OXM Metadata file. Using this approach, you can take advantage of EclipseLink's robust mappings framework and customize how each complex type in XML maps to its Java counterpart. The following API on DynamicJAXBContextFactory can be used to bootstrap from an OXM file:

/**
 * Create a <tt>DynamicJAXBContext</tt>, using an EclipseLink OXM file as the metadata source.
 *
 * @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>.  This map must
 *      contain a key of JAXBContext.ECLIPSELINK_OXM_XML_KEY, with a value of...
 *
 * @return
 *      A new instance of <tt>DynamicJAXBContext</tt>.
 *
 * @throws JAXBException
 *      if an error was encountered while creating the <tt>DynamicJAXBContext</tt>.
 */
public static DynamicJAXBContext createContextFromOXM(ClassLoader classLoader, Map<String, ?> properties) throws JAXBException {

Links to the actual OXM files are passed in via the properties parameter, using a special key, JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY. The value of this key will be a handle to the OXM metadata file, in the form of one of the following:

  • java.io.File
  • java.io.InputStream
  • java.io.Reader
  • java.net.URL
  • javax.xml.stream.XMLEventReader
  • javax.xml.stream.XMLStreamReader
  • javax.xml.transform.Source
  • org.w3c.dom.Node
  • org.xml.sax.InputSource

Lists of the above inputs are acceptable as well, to bootstrap from multiple OXM files. For more information, see the documentation on the DynamicJAXBContextFactory class.

In the following example, we will obtain our OXM file as a resource from our ClassLoader, and use the resulting InputStream to bootstrap a DynamicJAXBContext:

InputStream iStream = myClassLoader.getResourceAsStream("mynamespace/resources/eclipselink/eclipselink-oxm.xml");
 
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, iStream);
 
DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(myClassLoader, properties);



Example

Using the following example OXM, we will show an example of how to create and marshall a new object using Dynamic MOXy:

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xs="http://www.w3.org/2001/XMLSchema" package-name="mynamespace">
 
    <java-types>
        <java-type name="Customer">
            <xml-root-element name="customer"/>
            <java-attributes>
                <xml-element java-attribute="firstName" type="java.lang.String"/>
                <xml-element java-attribute="lastName" type="java.lang.String"/>
                <xml-element java-attribute="address" type="mynamespace.Address"/>
            </java-attributes>
        </java-type>
 
        <java-type name="Address">
            <java-attributes>
                <xml-element java-attribute="street" type="java.lang.String"/>
                <xml-element java-attribute="city" type="java.lang.String"/>
                <xml-element java-attribute="province" type="java.lang.String"/>
                <xml-element java-attribute="postalCode" type="java.lang.String"/>
            </java-attributes>
        </java-type>
    </java-types>
 
</xml-bindings>

The following code demonstrates:

  • Passing the OXM file to DynamicJAXBContextFactory to create a DynamicJAXBContext
  • Creating new DynamicEntities and setting their properties
  • Creating a JAXBMarshaller and marshalling the Java objects to XML
InputStream iStream = myClassLoader.getResourceAsStream("mynamespace/resources/eclipselink/eclipselink-oxm.xml");
 
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, iStream);
 
DynamicJAXBContext jaxbContext = DynamicJAXBContextFactory.createContextFromOXM(myClassLoader, properties);
 
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", newAddress);
 
dContext.createMarshaller().marshal(newCustomer, System.out);



Bootstrapping from an EclipseLink Project

Dynamic MOXy also supports bootstrapping from an EclipseLink Project specified in sessions.xml

Note.png
The key thing to remember when bootstrapping from an EclipseLink project is that your project will only specify Java class names, and not actual Java classes. Keep in mind that you are mapping "imaginary" classes to XML, and these classes will be dynamically generated in memory when the DynamicJAXBContext is built.

CORRECT:

customerDescriptor.setJavaClassName("mynamespace.Customer");
...
addressMapping.setReferenceClassName("mynamespace.Address");

INCORRECT:

customerDescriptor.setJavaClass(mynamespace.Customer.class);
...
addressMapping.setReferenceClass(mynamespace.Address.class);


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 mynamespace;
 
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("mynamespace.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("mynamespace.Address");
      addressMapping.getNullPolicy().setNullRepresentedByXsiNil(true);
      addressMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.XSI_NIL);
      customerDescriptor.addMapping(addressMapping);
 
      XMLDescriptor addressDescriptor = new XMLDescriptor();
      addressDescriptor.setJavaClassName("mynamespace.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">mynamespace.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);
Idea.png
You may specify multiple Session names when bootstrapping from an EclipseLink Project. For example,
DynamicJAXBContextFactory.createContext("ProjectA:ProjectB", null, null);
would create a single DynamicJAXBContext that is aware of Mappings and Descriptors from both Projects.




Advanced Topics

Dynamic MOXy provides further functionality for more advanced use cases.



Bootstrapping from jaxb.properties

The JAXB Specification supports another way of creating JAXBContexts; by specifying a factory class in jaxb.properties and then using the JAXBContext.newInstance() API.

First, we create a jaxb.properties with the following contents, specifying our DynamicJAXBContextFactory as the factory used to build new JAXBContexts. Place the jaxb.properties on your classpath in the package that your DynamicEntites will be generated in (in our example, the mynamespace package).

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.dynamic.DynamicJAXBContextFactory

Now, you can use the newInstance(String contextPath) method to create a DynamicJAXBContext. When this method is called, MOXy attempts to locate a jaxb.properties file on the classpath in the package specified by contextPath, and use the specified factory.

DynamicJAXBContext jaxbContext = (DynamicJAXBContext) JAXBContext.newInstance("mynamespace");



Customizing Generated Mappings with EclipseLink Metadata

If you are bootstrapping from an XML Schema (or EclipseLink project), you can customize the mappings that EclipseLink generates by injecting your own EclipseLink OXM Bindings file, containing any additional mapping configurations you wish to make. This allows you to effectively combine OXM and XSD bootstrapping, allowing you to use EclipseLink mappings to customize an existing XML schema.

Taking our Customer / Address example, lets say that we wanted our XML to contain Addresses in US format, as opposed to the Canadian format that is defined in our schema. First, we create an eclipselink-oxm.xml file that contains the mapping overrides we want to make (in this case, modifying the XPaths for province and postalCode):

<?xml version="1.0" encoding="US-ASCII"?>
<xml-bindings xmlns="http://www.eclipse.org/eclipselink/xsds/persistence/oxm" package-name="mynamespace">
    <java-types>
        <java-type name="Address">
            <java-attributes>
                <xml-element java-attribute="province" xml-path="state/text()"/>
                <xml-element java-attribute="postalCode" xml-path="zip-code/text()"/>
            </java-attributes>
        </java-type>
    </java-types>
</xml-bindings>

Then, when we create a DynamicJAXBContext, in addition to our Schema we also pass along this binding file to the DynamicJAXBContextFactory via the properties argument:

// Load Schema
InputStream xsdStream = myClassLoader.getSystemResourceAsStream("mynamespace/resources/xsd/customer.xsd");
 
// Load OXM with customizations, put into Properties
InputStream oxmStream = myClassLoader.getSystemResourceAsStream("mynamespace/resources/eclipselink/eclipselink-oxm.xml");
Map<String, Object> props = new HashMap<String, Object>();
props.put(JAXBContextFactory.ECLIPSELINK_OXM_XML_KEY, oxmStream);
 
// Create Context
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, null, myClassLoader, props);



Customizing Generated Mappings with XJC External Binding Customization Files

When bootstrapping from an XSD, you have the option to customize the mappings that will be generated through the use of XJC's External Binding Customization file format (.xjb). In the example below, the package name of the dynamic classes has been overridden, and the name attribute has been renamed to last-name-comma-first-name.

custom1.xjb:

<jxb:bindings version="1.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
    <jxb:bindings schemaLocation="employee.xsd" node="/xs:schema">
 
        <!-- Customize the package name that is generated for each schema -->
        <jxb:schemaBindings>
            <jxb:package name="com.acme.internal"/>
        </jxb:schemaBindings>
 
        <!-- Rename the 'name' element to 'last-name-comma-first-name' -->
        <jxb:bindings node="//xs:complexType[@name='person']">
            <jxb:bindings node=".//xs:element[@name='name']">
                <jxb:property name="last-name-comma-first-name"/>
            </jxb:bindings>
        </jxb:bindings>
 
    </jxb:bindings>
</jxb:bindings>

For complete information on the External Binding Customization file format, please see http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/2.0/tutorial/doc/JAXBUsing4.html.

If you wish to use External Binding Customization files, you will need to use Source objects to point to your XML Schema. Sources are used to load the .xjb files as well, and they must all have the same System ID set. Below is an example of bootstrapping from an XSD, and customizing the mapping generation using two separate .xjb files.

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
String xsd = "mynamespace/resources/xsd/employee.xsd";
String xjb1 = "mynamespace/resources/xsd/custom1.xjb";
String xjb2 = "mynamespace/resources/xsd/custom2.xjb";
 
InputStream xsdStream = classLoader.getSystemResourceAsStream(xsd);
Source xsdSource = new StreamSource(xsdStream);
// Set SYSTEM_ID to the filename part of the XSD
xsdSource.setSystemId("employee.xsd");
 
InputStream xjbStream = classLoader.getResourceAsStream(xjb1);
Source xjbSource = new StreamSource(xjbStream);
// Set SYSTEM_ID to be the same as the XSD
xjbSource.setSystemId(xsdSource.getSystemId());
 
InputStream xjbStream2 = classLoader.getResourceAsStream(xjb2);
Source xjbSource2 = new StreamSource(xjbStream2);
// Set SYSTEM_ID to be the same as the XSD
xjbSource2.setSystemId(xsdSource.getSystemId());
 
ArrayList<Source> xjbFiles = new ArrayList<Source>(2);
xjbFiles.add(xjbSource);
xjbFiles.add(xjbSource2);
 
// Put XSD and XJBs into Properties
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(DynamicJAXBContextFactory.XML_SCHEMA_KEY, xsdSource);
properties.put(DynamicJAXBContextFactory.EXTERNAL_BINDINGS_KEY, xjbFiles);
 
// Create Context
DynamicJAXBContext jaxbContext = (DynamicJAXBContext) JAXBContext.newInstance("mynamespace", classLoader, properties);

The value of EXTERNAL_BINDINGS_KEY can be either a single Source or a List<Source>, pointing to your External Binding Customization file(s).



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:

<!-- customer.xsd -->
 
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:myns="myNamespace" xmlns:add="addressNamespace"
   xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="myNamespace">
 
    <xs:import namespace="addressNamespace" schemaLocation="address.xsd"/>
 
    <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="add:address"/>
        </xs:sequence>
    </xs:complexType>
 
</xs:schema>
<!-- address.xsd -->
 
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="addressNamespace"
   xmlns="addressNamespace" elementFormDefault="unqualified">
 
    <xsd: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>
    </xsd:complexType>
 
</xsd:schema>

In this case, we must supply an EntityResolver implementation which will be able to resolve the location of the imported schema. First, the EntityResolver code:

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;
   }
 
}

Now, when you create your DynamicJAXBContext, you can pass your EntityResolver in as well:

InputStream inputStream = ClassLoader.getSystemResourceAsStream("com/foo/sales/xsd/customer.xsd");
DynamicJAXBContext dContext = DynamicJAXBContextFactory.createContextFromXSD(inputStream, new MyEntityResolver(), null, null);
Warning2.png
If, when importing another schema, you see the following exception:
Internal Exception: org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema
document '<imported-schema-name>', because 1) could not find the document; 2) the document could
not be read; 3) the root element of the document is not <xsd:schema>.

then you should try disabling XJC's "schema correctness check" by setting the following Java property:

In Code:

System.setProperty("com.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck", "true")

Command Line:

-Dcom.sun.tools.xjc.api.impl.s2j.SchemaCompilerImpl.noCorrectnessCheck=true