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/DesignDocs/217508
Support for Root Elements representing Datatypes
Document History
Date | Author | Version Description & Notes |
---|---|---|
2008-02-01 | Rick Barkhouse | Initial draft. |
2008-02-29 | Rick Barkhouse | Added prototype information. |
Project overview
This project will add the ability for EclipseLink MOXy to unmarshal XML documents that have datatypes as their root element. Instead of a "rich" top-level element representing a domain object (e.g. Employee
, PurchaseOrder
), the root element may be a datatype value, such as String
or Byte[]
.
Goals:
- Allow the user to unmarshal XML documents that contain simple type root elements.
Concepts
The following concepts are used in this document:
- Simple Type - one of the basic, "built-in" types available in XML Schema. These may represent basic Java primitives (such as
xsd:boolean
andxsd:float
), or more robust types (xsd:hexBinary
,xsd:dateTime
,xsd:QName
). XML Schema has two categories of simple types; Primitive Types (the most basic of data type), and Derived Types (other simple types that are defined in terms of primitive types).
XML Schema Primitive Data Types | ||||||
---|---|---|---|---|---|---|
string | boolean | decimal | float | double | duration | dateTime |
time | date | gYearMonth | gYear | gMonthDay | gDay | gMonth |
hexBinary | base64Binary | anyURI | QName | NOTATION | ||
XML Schema Derived Data Types | ||||||
normalizedString | token | language | NMTOKEN | NMTOKENS | Name | NCName |
ID | IDREF | IDREFS | ENTITY | ENTITIES | integer | nonPositiveInteger |
negativeInteger | long | int | short | byte | nonNegativeInteger | unsignedLong |
unsignedInt | unsignedShort | unsignedByte | positiveInteger |
For more information see: http://www.w3.org/TR/xmlschema-2/#built-in-datatypes
Requirements
The requirements for this project are as follows:
- Support unmarshalling of XML instance documents that contain Simple Type (both primitive and derived) root elements.
- Support marshalling "primitive" Java objects to XML.
- Ensure that the proper
ConversionManager
is used when converting XML to Java. - Ensure that the proper
ClassLoader
is used when converting values.
For example, the JAXB TCK uses the following types of test documents:
XML Schema - The schema contains a single element, a restriction (extension) of the base64Binary
simple type:
<schema ...> ... <element name="NISTSchema-base64Binary-enumeration" type="nist:NISTSchema-base64Binary-enumeration-Type"/> <simpleType name="NISTSchema-base64Binary-enumeration-Type"> <restriction base="base64Binary"> <enumeration value="bHlsY2JmaXFjaW9ubmg="/> </restriction> </simpleType> </schema>
XML Instance Document - The document contains a single element with value zGk=
:
<NISTSchema-base64Binary-enumeration xmlns="NISTSchema-base64Binary-enumeration-NS" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="NISTSchema-base64Binary-enumeration-NS enumeration.xsd">zGk=</NISTSchema-base64Binary-enumeration>
Functionality
Schema (myDatatypeSchema.xsd
):
... <element name="aValue" type="myDatatype"/> <simpleType name="myDatatype"> <restriction base="double"> <enumeration value="4"/> <enumeration value="8"/> <enumeration value="15"/> <enumeration value="16"/> <enumeration value="23"/> <enumeration value="42"/> </restriction> </simpleType> ...
Use Case 1: Root Element is a Datatype
Instance Document (instance-1.xml
):
<?xml version="1.0"?> <aValue>16</aValue>
EclipseLink Code:
import org.eclipse.persistence.oxm.XMLContext; ... MyProject proj = new MyProject(); XMLContext ctx = new XMLContext(proj); Object o = ctx.createUnmarshaller().unmarshal(new File("instance-1.xml")); System.out.println("OBJECT: " + o + " ("+ o.getClass() + ")"); ...
Result:
OBJECT: 16.0 (class java.lang.Double)
Use Case 2: Element Type Doesn't Match Schema
Instance Document (instance-2.xml
):
<?xml version="1.0"?> <aValue xsi:type="date">16</aValue>
Use Case 3: Unexpected Content
Instance Document (instance-3.xml
):
<?xml version="1.0"?> <aValue> 16 <other>Something Else</other> Hello World! </aValue>
Use Case 4: Datatype is a Union
Instance Document (instance-4.xml
):
<?xml version="1.0"?>
Prototype
Following is an overview of the prototype implementation.
XMLDatatypeDescriptor
The information required to work with primitive data types is encapsulated in a new class, XMLDatatypeDescriptor
. This is far less complicated than the other types of Eclipselink descriptors, storing only the following data:
- the element's
QName
- the element's XSD schema type (also a
QName
) - the Java class that the value will be converted into
// org.eclipse.persistence.oxm.XMLDatatypeDescriptor ... public class XMLDatatypeDescriptor { // Element's QName; also used as the key in XMLContext's datatypesByQName Map private QName qName; // Java class this element will be converted to private Class javaClass; // Element's XSD Type private QName schemaType; ... }
XMLContext's datatypesByQName Map
A hash map is added to XMLContext
to keep track of which user-defined types represent restrictions ("subclasses") of XML primitive types. The values of this map are XMLDatatypeDescriptor
s, keyed on the element's QName
.
// org.eclipse.persistence.oxm.XMLContext public class XMLContext { ... private Map<QName, XMLDatatypeDescriptor> datatypesByQName; ... }
During unmarshal operations, if the QName
of the element being unmarshalled is present in datatypesByQName
, a new XMLRootRecord
is created. XMLRootRecord
is a SAX ContentHandler, and control will be passed to it to unmarshal the primitive element:
// org.eclipse.persistence.internal.oxm.record.SAXUnmarshallerHandler public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { ... XMLDescriptor xmlDescriptor = xmlContext.getDescriptor(rootQName); ... if (null == xmlDescriptor) { // Check to see if there is an XMLDatatypeDescriptor for this QName XMLDatatypeDescriptor wrapper = (XMLDatatypeDescriptor) xmlContext.getDatatypesByQName().get(rootQName); if (wrapper != null) { XMLRootRecord rootRecord = new XMLRootRecord(wrapper.getJavaClass(), this, wrapper.getSchemaType()); rootRecord.setSession((AbstractSession) xmlContext.getSession(wrapper)); rootRecord.startElement(namespaceURI, localName, qName, atts); xmlReader.setContentHandler(rootRecord); return; } } ... }
This map is populated in one of three ways:
1. (SDO) When the user defines a schema using SDOXSDHelper.define()
, a new XMLDatatypeDescriptor will be created and put into the map during type generation:
// org.eclipse.persistence.sdo.helper.SDOTypesGenerator private void addRootElementToDescriptor(SDOProperty p, String targetNamespace, String xsdName) { if (!p.getType().isDataType()) { ... } else { // This must be a primitive, so add to XMLContext's primitivesByQName map SDOXMLHelper helper = (SDOXMLHelper) ((SDOType)p.getType()).getHelperContext().getXMLHelper(); QName qn = new QName(targetNamespace, xsdName); Class primitiveClass = p.getType().getInstanceClass(); QName xsdTypeQN = ((SDOType)p.getType()).getXsdType(); XMLDatatypeDescriptor wrapper = new XMLDatatypeDescriptor(qn, primitiveClass, xsdTypeQN); helper.getXmlContext().getDatatypesByQName().put(qn, wrapper); }
2. (SDO) When the user specifies types directly, using SDOTypeHelper.define()
, a new XMLDatatypeDescriptor will be put into the map in defineOpenContentProperty():
// org.eclipse.persistence.sdo.helper.delegates.SDOTypeHelperDelegate private void defineOpenContentProperty(String propertyUri, String propertyName, Property property) { if (propertyUri != null) { ... XMLDescriptor aDescriptor = ((SDOType)property.getType()).getXmlDescriptor(); ... if (aDescriptor != null) { ... } else { // This must be a primitive, so add to XMLContext's primitivesByQName map SDOXMLHelper helper = (SDOXMLHelper) ((SDOType)property.getType()).getHelperContext().getXMLHelper(); Class primitiveClass = property.getType().getInstanceClass(); QName xsdTypeQN = ((SDOType)property.getType()).getXsdType(); XMLDatatypeDescriptor wrapper = new XMLDatatypeDescriptor(propertyQName, primitiveClass, xsdTypeQN); helper.getXmlContext().getDatatypesByQName().put(propertyQName, wrapper); } } }
3. When the user explicitly adds primitive type information to XMLLogin
in code:
XMLLogin xmlLogin = new XMLLogin(); Project oxmProject = new Project(xmlLogin); QName qname = new QName("myBase64Binary-NS", "myBase64Binary"); XMLDatatypeDescriptor desc = new XMLDatatypeDescriptor(qname, Byte[].class, XMLConstants.BASE_64_BINARY_QNAME); xmlLogin.addXMLDatatypeDescriptor(desc); XMLContext xmlContext = new XMLContext(oxmProject); ...
Project Metadata
XMLDatatypeDescriptors
are persisted to XML as part of the XMLLogin
element, and are accessed through the public API on XMLLogin
shown above. An example of a project containing primitive information:
<?xml version="1.0" encoding="UTF-8"?> <eclipselink:object-persistence version="Eclipse Persistence Services - 1.0 (Build SNAPSHOT - 20080227)" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:eclipselink="http://xmlns.oracle.com/ias/xsds/eclipselink"> <eclipselink:name>My OXM Project</eclipselink:name> <eclipselink:login xsi:type="eclipselink:xml-login"> <eclipselink:platform-class>org.eclipse.persistence.oxm.platform.SAXPlatform</eclipselink:platform-class> <eclipselink:user-name></eclipselink:user-name> <eclipselink:password></eclipselink:password> <eclipselink:datatype-descriptors> <eclipselink:datatype-descriptor> <eclipselink:java-class>java.lang.Float</eclipselink:java-class> <eclipselink:schema-type>{http://www.w3.org/2001/XMLSchema}float</eclipselink:schema-type> <eclipselink:qname>{myFloat-NS}myFloat</eclipselink:qname> </eclipselink:datatype-descriptor> </eclipselink:datatype-descriptors> </eclipselink:login> </eclipselink:object-persistence>
Documentation
EclipseLink User Documentation should be updated to demonstrate how documents containing Simple Type root elements are supported.
Open Issues
Issue # | Owner | Description / Notes |
---|
Issue # | Description / Notes | Decision |
---|