Skip to main content
Jump to: navigation, search

EclipseLink/Development/2.1/DynamicMOXy/296967/BootstrapFromXSD

< EclipseLink‎ | Development‎ | 2.1‎ | DynamicMOXy/296967
Revision as of 16:34, 30 March 2010 by Rick.barkhouse.oracle.com (Talk | contribs) (Example Schemas)

Dynamic MOXy - Bootstrapping from XML Schema

Overview

Support for bootstrapping a DynamicJAXBContext from an XML Schema file (XSD) will be implemented in multiple phases. For each phase, test schemas will be created that would result in the following annotations being generated by XJC. A DynamicJAXBContext will then be created from each schema, and an example object will be marshalled. The resulting document will be inspected to ensure that it was marshalled properly, according to the annotations being tested.

Generating a DynamicJAXBContext with XJC

A DynamicJAXBContext is created from an XSD in the following way:

  1. The schema is parsed using com.sun.tools.xjc.api.SchemaCompiler, resulting in a com.sun.codemodel.JCodeModel being generated.
  2. JCodeModel contains XJC representations (com.sun.codemodel.JDefinedClass) of the classes to be generated. We iterate over the collection of JDefinedClasses to create EclipseLink representations of the same classes (org.eclipse.persistence.jaxb.javamodel.xjc.*)
  3. This collection of EclipseLink javamodel classes is then passed to org.eclipse.persistence.jaxb.compiler.Generator, which generates a standard EclipseLink project.
  4. A Dynamic Project is then created from the standard project via DynamicTypeBuilder.loadDynamicProject().
  5. An org.eclipse.persistence.oxm.XMLContext is then created from the Dynamic Project, and a DynamicJAXBContext is returned backed by this XMLContext.


Development Phases

Phase 1

Annotation XML Metadata Tag Package Type Field Method
XmlNs xml-ns        
XmlSchema xml-schema X      
XmlSeeAlso xml-see-also   X    
XmlTransient xml-transient   X X X


The following example schema demonstrates how these annotations will be derived from the schema:

Example Schema

<?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="person" type="myns:person"/>
 
    <xs:complexType name="person">
        <xs:sequence>
            <xs:element name="name" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
 
    <xs:complexType name="customer">
        <xs:complexContent>
            <xs:extension base="myns:person">
                <xs:sequence>
                    <xs:element name="customer-id" type="xs:int"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
 
    <xs:complexType name="employee">
        <xs:complexContent>
            <xs:extension base="myns:person">
                <xs:sequence>
                    <xs:element name="employee-id" type="xs:string"/>
                </xs:sequence>
            </xs:extension>
        </xs:complexContent>
    </xs:complexType>
 
</xs:schema>
  • The targetNamespace and elementFormDefault property will result in an XmlSchema annotation on the generated package-info class.
  • The inheritance relationship between person, customer, and employee will result in an XmlSeeAlso annotation on the generated Person class.

Limitations

  • XJC does not interpret the attributeFormDefault property during schema parsing, and will therefore not generate this property in the XmlSchema annotation.
  • There is no XML Schema construct that will result in an XmlTransient annotation being generated.



Phase 2

Annotation XML Metadata Tag Package Type Field Method
XmlAccessorOrder xml-accessor-order X X    
XmlAccessorType xml-accessor-type X X    
XmlRootElement xml-root-element   X    
XmlType xml-type   X    


The following example schema demonstrates how these annotations will be derived from the schema:

Example Schema

<?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="individuo" type="myns:person"/>
 
    <xs:complexType name="person">
        <xs:sequence>
            <xs:element name="id" type="xs:int"/>
            <xs:element name="first-name" type="xs:string"/>
            <xs:element name="last-name" type="xs:string"/>
            <xs:element name="phone-number" type="xs:string"/>
            <xs:element name="email" type="xs:string"/>            
        </xs:sequence>
    </xs:complexType>
 
</xs:schema>
  • The name property of the root element will appear in the XmlRootElement annotation's name property.
  • The order of the elements of the person type will be captured in the XmlType annotation's propOrder property.

Limitations

  • There is no XML Schema construct that will result in the generation of XmlAccessorOrder or XmlAccessorType annotations.



Phase 3

Annotation XML Metadata Tag Package Type Field Method
XmlAttribute xml-attribute     X X
XmlElement xml-element     X X
XmlAdapter xml-java-type-adapter X X X X


The following example schema demonstrates how these annotations will be derived from the schema:

Example Schema

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="myNamespace" xmlns:myns="myNamespace" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
    <xs:element name="person" type="myns:person"/>
 
    <xs:complexType name="person">
        <xs:sequence>
            <xs:element name="type" type="xs:string"/>
        </xs:sequence>
        <xs:attribute name="id" type="xs:int"/>
    </xs:complexType>
 
</xs:schema>
  • The type field of the generated class will be annotated with @XmlElement.
  • The id field of the generated class will be annotated with @XmlAttribute.

Limitations

  • There is no XML Schema construct that will result in the generation of an XmlAdapter annotation.



Phase 4

Annotation XML Metadata Tag Package Type Field Method
XmlElementWrapper xml-element-wrapper     X X
XmlList xml-list     X X
XmlValue xml-value X X
XmlAnyElement xml-any-element     X X
XmlAnyAttribute xml-any-attribute     X X
XmlMixed xml-mixed     X X


The following example schema demonstrates how these annotations will be derived from the schema:

Example Schemas

<!-- XmlList -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
 
    <xs:element name="person">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"/>
                <xs:element name="codes" type="myns:codes"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
    <xs:simpleType name="codes">
        <xs:list itemType="xs:string"/>
    </xs:simpleType>
 
</xs:schema>
  • An xs:simpleType containing an xs:list results in an XmlList annotation on the codes field
<!-- XmlValue -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
 
    <xs:element name="person" type="myns:person"/>
 
    <xs:complexType name="person">
        <xs:sequence>
            <xs:element name="name" type="xs:string"/>
            <xs:element name="salary" type="myns:cdn_currency"/>
        </xs:sequence>
    </xs:complexType>
 
    <xs:complexType name="cdn_currency">
        <xs:simpleContent>
            <xs:extension base="xs:decimal"/>
        </xs:simpleContent>
    </xs:complexType>
 
</xs:schema>
  • A complexType containing simpleContent results in an XmlValue annotation on the value field of the CdnCurrency type.
<!-- XmlAnyElement / XmlAnyAttribute -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
 
    <xs:element name="person">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="name" type="xs:string"/>
                <xs:any/>
            </xs:sequence>
            <xs:anyAttribute/>
        </xs:complexType>
    </xs:element>
 
</xs:schema>
  • The xs:any element results in the generation of an any field on Person, annotated with XmlAnyElement.
  • The xs:anyAttribute element results in the generation of an otherAttributes field on Person, annotated with XmlAnyAttribute.
<!-- XmlMixed -->
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema ...>
 
    <xs:element name="person">
        <xs:complexType mixed="true">
            <xs:sequence>
                <xs:element name="title" type="xs:string"/>
                <xs:element name="name" type="xs:string"/>
                <xs:element name="rewardPoints" type="xs:positiveInteger"/>                
            </xs:sequence>
        </xs:complexType>
    </xs:element>
 
</xs:schema>
  • mixed=true results in the generation of a content field on Person, annotated with XmlMixed (as well as XmlElementRefs for each possible value)

Limitations

  • There is no XML Schema construct that will result in an XmlElementWrapper annotation being generated.



Phase 5

Annotation XML Metadata Tag Package Type Field Method Schema Representation
XmlID
xml-id     X X
public class Customer {
   @XmlAttribute
   @XmlID
   public String getCustomerID();
   public void setCustomerID(String id);
}
 
<xs:complexType name="Customer">
   <xs:complexContent>
      <xs:sequence>
         ....
      </xs:sequence>
      <xs:attribute name="customerID" type="xs:ID"/>
   </xs:complexContent>
</xs:complexType>
XmlIDREF
xml-idref   X
X
public class Shipping {
   @XmlIDREF
   public Customer getCustomer();
   public void setCustomer(Customer customer);
   ....
}
 
<xs:complexType name="Shipping">
   <xs:complexContent>
      <xs:sequence>
         <xs:element name="customer" type="xs:IDREF"/>
         ....
      </xs:sequence>
   </xs:complexContent>
</xs:complexType>



Phase 6

Annotation XML Metadata Tag Package Type Field Method Schema Representation
XmlElements xml-elements     X X
public class Foo {
   @XmlElements(
      @XmlElement(name="A", type=Integer.class),
      @XmlElement(name="B", type=Float.class)
   }
   public List items;
}
 
<xs:complexType name="Foo">
   <xs:sequence>
      <xs:choice minOccurs="0" maxOccurs="unbounded">
         <xs:element name="A" type="xs:int"/>
         <xs:element name="B" type="xs:float"/>
      <xs:choice>
   </xs:sequence>
</xs:complexType>
XmlElementRef xml-element-ref     X X
@XmlRootElement(name="target")
class Target {
   // The presence of @XmlElementRef indicates that the XML
   // element name will be derived from the @XmlRootElement 
   // annotation on the type (for e.g. "jar" for JarTask). 
   @XmlElementRef
   List<Task> tasks;
}
 
abstract class Task {}
 
@XmlRootElement(name="jar")
class JarTask extends Task {
   ...
}
 
@XmlRootElement(name="javac")
class JavacTask extends Task {
   ...
}
 
<xs:element name="target" type="Target">
   <xs:complexType name="Target">
      <xs:sequence>
         <xs:choice maxOccurs="unbounded">
            <xs:element ref="jar">
            <xs:element ref="javac">
         </xs:choice>
      </xs:sequence>
   </xs:complexType>
XmlElementRefs xml-element-refs X
X
N/A ?

Referenced in XJC code but unsure how it is specified from Schema



Phase 7

Annotation XML Metadata Tag Package Type Field Method Schema Representation
XmlSchemaType xml-schema-type X   X X
public class USPrice {
   @XmlElement
   @XmlSchemaType(name="date")
   public XMLGregorianCalendar date;
}
 
<xs:complexType name="USPrice"/>
   <xs:sequence>
      <xs:element name="date" type="xs:date"/>
   </sequence>
</xs:complexType>
XmlSchemaTypes xml-schema-types X    
 
N/A



Phase 8

Annotation XML Metadata Tag Package Type Field Method Schema Representation
XmlEnum xml-enum   X    
@XmlEnum(String.class)
public enum Card { CLUBS, DIAMONDS, HEARTS, SPADES }
 
<xs:simpleType name="Card">
   <xs:restriction base="xs:string"/>
      <xs:enumeration value="CLUBS"/>
      <xs:enumeration value="DIAMONDS"/>
      <xs:enumeration value="HEARTS"/>
      <xs:enumeration value="SPADES"/>
</xs:simpleType>
XmlEnumValue xml-enum-value     X
X
N/A



Phase 9

Annotation XML Metadata Tag Package Type Field Method Schema Representation
XmlInlineBinaryData xml-inline-binary-data   X X X N/A



Phase 10

Annotation XML Metadata Tag Package Type Field Method Schema Representation
XmlElementDecl xml-element-decl     X X N/A ?

Only used for ObjectFactory.java

XmlRegistry xml-registry   X     N/A ?

Only used for ObjectFactory.java


Design

When constructing a DynamicJAXBContext from XML Schema, we can use APIs from the Java XJC compiler to parse a schema and create Java class definitions (XJC's JCodeModel) in memory, then pass these class definitions to an EclipseLink Generator (org.eclipse.persistence.jaxb.compiler.Generator) to generate an EclipseLink project. After we have created this project, we can use the DynamicTypeBuilder to create a dynamic project, generating Java classes in memory along the way.

First, we use XJC API to parse an XSD and generate a JCodeModel. Note that this code stops short of actually generating .java files, it only generates an in-memory representation of the Java files that would normally be created.

// Use XJC API to parse the schema and generate its JCodeModel
SchemaCompiler sc = XJC.createSchemaCompiler();
InputSource inputSource = new InputSource(schemaInputStream);
sc.parseSchema(inputSource);
S2JJAXBModel model = sc.bind();
JCodeModel jCodeModel = model.generateCode(new Plugin[0], null);

We can then wrap these XJC classes in our own implentations of EclipseLink's JAXB JavaModel classes. This will allow us to use the Generator to create an EclipseLink project and mappings. The JavaModel interfaces define "wrappers" for the various Java language constructs that make up the domain model (e.g. classes, methods, constructors, annotations, packages, etc). For example:

public class XJCJavaFieldImpl implements JavaField {
 
    // XJC's definition of a Field
    protected JFieldVar xjcField;
 
    ...
 
    public int getModifiers() {
        return xjcField.mods().getValue();
    }
 
    public String getName() {
        return xjcField.name();
    }
 
    ...
}

Creating the JavaModel classes:

// Create EclipseLink JavaModel objects for each of XJC's JDefinedClasses
ArrayList<JDefinedClass> classesToProcess = new ArrayList<JDefinedClass>();
Iterator<JPackage> packages = jCodeModel.packages();
while (packages.hasNext()) {
   JPackage pkg = packages.next();
   Iterator<JDefinedClass> classes = pkg.classes();
   while (classes.hasNext()) {
      JDefinedClass cls = classes.next();
      classesToProcess.add(cls);
   }
}   
JavaClass[] jotClasses = createClassModelFromXJC(classesToProcess, jCodeModel);

At this point, we can instantiate a Generator and obtain a "dry" EclipseLink project, which we can then turn into a dynamic project with generated in-memory classes. Finally, we create and store an XMLContext and a DynamicHelper:

// Use the JavaModel to setup a Generator to generate an EclipseLink project 
XJCJavaModelImpl javaModel = new XJCJavaModelImpl(Thread.currentThread().getContextClassLoader(), jCodeModel);
XJCJavaModelInputImpl javaModelInput = new XJCJavaModelInputImpl(jotClasses, javaModel);
Generator g = new Generator(javaModelInput);
Project p = g.generateProject();
 
// Make a Dynamic Project from this project, because these classes do not exist on the classpath
DynamicClassLoader dynamicClassLoader;
if (classLoader instanceof DynamicClassLoader) {
   dynamicClassLoader = classLoader;
} else {
   dynamicClassLoader = new DynamicClassLoader(classLoader);            
}
Project dp = DynamicTypeBuilder.loadDynamicProject(p, null, dynamicClassLoader);
 
this.xmlContext = new XMLContext(dp);
this.dynamicHelper = new DynamicHelper(xmlContext.getSession(0));

Back to the top