This article explains the LuciadLightspeed XML framework.
-
Introduction to XML gives a short introduction to XML and the XML framework.
-
Decoding XML documents describes how to decode and encode XML documents.
-
Decoding XML documents describes how to decode and encode XML documents.
-
Creating a data model based on an XML schema explains how to derive a data model from an XML schema.
-
Custom XML decoding shows how to customize the XML decoding.
-
Advanced features explains some other advanced features that are not handled in the other sections.
Introduction to XML
The XML binding framework allows you to convert XML documents into Java content trees and the other way around. The framework
is introduced to support other LuciadLightspeed XML-based formats, such as GML (discussed in
Integrating GML data into your application), AIXM and OGC Filter. The com.luciad.format.xml.bind
, com.luciad.format.xml.bind.schema
, and format.xml.bind.schema.dataobject
are the main packages.
The new framework replaces the deprecated format.xml
framework.
The new framework is more efficient and easier to use and also provides additional functionality.
The current LuciadLightspeed implementation is based on the XML 1.0 specification, which you can find at W3C’s website (http://www.w3.org/TR/REC-xml/). Reading and writing XML data is done using the streaming API for XML (StAX). This article is not meant to be an XML course; it assumes the reader has a thorough understanding of XML and XML schema, and at least a basic knowledge of StAX.
Creating a Java content tree from an XML document is called unmarshalling, the other way around is called marshalling. In this article, these terms are used interchangeably with decoding and encoding. Conceptually encoding and decoding are treated in a very similar fashion implemented with parallel classes (typically for each encoding class there is a similar decoding class). Because of that, the focus of the rest of this article is only on decoding.
This article focuses on documents based on a XML schema as all supported XML-based formats in LuciadLightspeed are based on
a schema. The framework can also handle documents without a schema. The API Reference of the package com.luciad.format.xml.bind
contains more information about this.
The XML binding framework is tightly integrated with the unified domain object approach described in Unified object access. This article assumes you have a good understanding of that article.
Representing schema information
Because an XML schema is such an important aspect of most standardized XML formats, LuciadLightspeed introduces dedicated classes to represent information about a schema.
-
The framework uses
TLcdXMLSchemaTypeIdentifier
andTLcdXMLSchemaElementIdentifier
instances to uniquely identify XML schema types and elements. -
The framework uses
TLcdXMLSchemaType
andTLcdXMLSchemaElement
instances to represent information about substitution groups. -
TLcdXMLBuiltInDataTypes
,TLcdXMLBuiltInConstants
, andTLcdXLinkDataTypes
define public constants that give immediate access to the types, elements, and qualified names (QName
) defined in the XMLSchema and XLink schema.
Decoding XML documents
Use a TLcdXMLSchemaBasedDecoder
to decode schema-based XML documents. In order to create the right domain objects, you need to configure the decoder so that
it knows the structure of the document and how to map it on the Java domain object classes. For data models that are based
on an XML schema, this is straightforward. Program: Decoding ISO19115 XML documents shows this for the ISO19115 data model.
You can find a more detailed explanation of ISO 19115 in Working with ISO metadata. |
First a decoder is created and then it is configured for the ISO19115 data model. This makes the decoder ready to decode ISO 19115 documents.
TLcdXMLSchemaBasedDecoder decoder = new TLcdXMLSchemaBasedDecoder();
decoder.configure( TLcdISO19115DataTypes.getDataModel() );
return decoder.decode( "document.xml" );
The XML framework also provides the more basic interface ILcdXMLSchemaBasedDecoderLibrary
. A library is used when no such data model is available. You can find a more detailed explanation of how these libraries
work in the API Reference.
You can control the decoding process of the TLcdXMLSchemaBasedDecoder
as follows:
-
Use the
ILcdInputStreamFactory
to control the creation of theInputStream
instances that the decoder uses. -
Use the
XMLInputFactory
to control the creation of theXMLStreamReader
that is used by the decoder. -
Use the
EntityResolver2
to resolve schema documents. You can find theEntityResolver2
on theTLcdXMLSchemaBasedMapping
associated with the decoder.
Creating a data model based on an XML schema
This section explains how to create and customize a data model for an XML schema. Refer to Unified object access for more information on data models.
Mapping XML types on Java classes
The main responsibility of the TLcdXMLDataModelBuilder
class is to create a data model of which the data types map on the types defined in an XML schema. Given an XML schema, this
class creates a data model according to a number of pre-defined rules. The most important rules are the following:
-
The builder creates a data type in the data model for each type defined in the XML schema. This data type will have the same name as the XML schema type. For anonymous types, a unique name is derived from the location where the type is defined.
-
Complex types map on data object types, simple types on primitive types.
-
Type inheritance in XML schema maps directly on type inheritance. In other words, two types that inherit from each other in XML schema are mapped as two types that inherit from each other in the data model.
-
The following list shows how complex content maps on properties:
-
Attributes: typically one property per attribute
-
Elements: typically one property per element declaration
-
Character content: one property
-
-
Multi-valued elements (elements which have a
maxOccurence
larger than 1) map on list properties. -
All data types inherit the instance class from their parent type. The instance class for the XML schema type
anyType
isILcdDataObject
, foranySimpleType
it isObject
.
You can find the complete and detailed list of rules in the API Reference of the TLcdXMLDataModelBuilder
class.
To illustrate the use of the decoder, have a look at the schema shown in Program: A simple XML schema.
This schema defines two types: the anonymous type of the Model
element and the global Address
type.
<xsd:element name="Model">
<xsd:complexType>
<xsd:sequence>
<xsd:element ref="tns:address" minOccurs="0" maxOccurs="unbounded" />
</xsd:sequence>
</xsd:complexType>
</xsd:element>
<xsd:element name="address" type="tns:AddressType" />
<xsd:complexType name="AddressType">
<xsd:sequence>
<xsd:element name="street" type="xsd:string" />
<xsd:element name="number" type="xsd:int" />
<xsd:element name="city" type="xsd:string" />
</xsd:sequence>
</xsd:complexType>
TLcdXMLDataModelBuilder
maps the anonymous type of the Model
element on a type named _Model
with a single list property of type Address
. Note that the name of this data type starts with an underscore to indicate that it maps on the anonymous type of a global
element. The Address
type is mapped on a data type named Address
. This data type has three properties: street
(a String), number
(an Integer) and city
(a String).
The TLcdXMLDataModelBuilder
annotates all data models it creates with TLcdXMLSchemaMappingAnnotation
and TLcdXMLSchemaTypeMappingAnnotation
annotations. These annotations carry the XML mapping and XML schema information.
Program: Creating a data model for an XML schema shows how to create a data model based on the sample schema.
private static TLcdDataModel createDataModel() {
TLcdXMLDataModelBuilder builder = new TLcdXMLDataModelBuilder();
return builder.createDataModel("http://www.luciad.com/samples.xml", "path/to/schema.xsd");
}
Once the data model has been created, you can configure a decoder with it. This ensures that the decoder uses the appropriate domain classes during decoding. Program: Decoding XML documents shows this for the sample data model.
public Object decode(String document) throws IOException, XMLStreamException {
TLcdXMLSchemaBasedDecoder decoder = new TLcdXMLSchemaBasedDecoder();
decoder.configure(createDataModel());
return decoder.decode(document);
}
Custom domain classes
In many cases, you want to use dedicated domain classes for certain XML schema types. That way, you can implement certain
interfaces and add methods to make your domain classes easier to use. If the domain class implements ILcdDataObject
and follows the mapping rules of TLcdXMLDataModelBuilder
then you only need to set the instance class of the corresponding type.
Program: Using custom domain classes shows how custom domain classes are configured for the xml.customdomainclasses
sample. First a TLcdDataModelBuilder
is created. This builder is passed to a TLcdXMLDataModelBuilder
which creates all types and properties for the given XML schema. Then the data model builder is used to set the instance
classes for the Address
and _Model
types to the domain classes Address
and Model
. Both classes extend TLcdDataObject
.
samples/xml/customdomainclasses/CustomDomainClassesDataTypes
)
private static TLcdDataModel createDataModel() {
TLcdXMLDataModelBuilder builder = new TLcdXMLDataModelBuilder();
TLcdDataModelBuilder dataModelBuilder = new TLcdDataModelBuilder("sample");
builder.buildDataModel(dataModelBuilder, "http://www.luciad.com/samples.xml.customdomainclasses",
Main.class.getResource("/samples/xml/customdomainclasses/samples.xml.customdomainclasses.xsd").toString());
dataModelBuilder.typeBuilder("_Model").instanceClass(Model.class);
dataModelBuilder.typeBuilder("AddressType").instanceClass(Address.class);
return dataModelBuilder.createDataModel();
}
Custom XML decoding
You can also fully customize the XML mapping, encoding and decoding process. This allows you, for example, to have domain
classes which do not implement ILcdDataObject
or to provide custom code for decoding.
Custom simple types
A typical use case is to provide a custom domain class for an XML schema simple type. You can do this by performing the following four steps:
-
Set the instance class of the type to the custom domain class. This is exactly the same as shown in Program: Using custom domain classes.
-
Provide an instance of
ILcdXMLDatatypeUnmarshaller
. This interface defines the contract for unmarshalling XML simple types. Implementations of this interface basically transform text content into a Java object. -
Define a custom
ILcdXMLSchemaBasedDecoderLibrary
. Such a library is responsible for the configuration ofTLcdXMLSchemaBasedDecoder
instances for the data model. This is done by extending the defaultTLcdXMLDataObjectDecoderLibrary
class with additional code that registers the customILcdXMLDatatypeUnmarshaller
. -
Associate the custom decoder library with the data model by adding a
TLcdXMLSchemaMappingAnnotation
on the data model.
The xml.binding.custom
sample illustrates this. It defines the XML schema type ColorType
as a simple type that extends xsd:int
. In the Java domain model, this ColorType
is mapped on the java.awt.Color
class. Program: Custom marshalling and unmarshalling for the ColorType
shows the datatype unmarshaller for the Color
class. Note that this fragment also shows the implementation of the ILcdXMLDatatypeMarshaller
that is required for encoding.
ColorType
(from samples/xml/customdecodingencoding/ColorDatatypeAdapter
)
public class ColorDatatypeAdapter implements ILcdXMLDatatypeMarshaller<Color>, ILcdXMLDatatypeUnmarshaller<Color> {
@Override
public String marshal(Color aValue, XMLStreamWriter aWriter,
ILcdXMLDocumentContext aContext) throws XMLStreamException {
return Integer.toString(aValue.getRGB());
}
@Override
public Color unmarshal(String aLexicalValue, XMLStreamReader aReader,
ILcdXMLDocumentContext aContext) throws XMLStreamException {
return new Color(Integer.parseInt(aLexicalValue));
}
}
Program: A custom decoder library defines the CustomDecoderLibrary
as an extension of the TLcdXMLDataObjectDecoderLibrary
. The class overrides the doConfigure
method and registers a new instance of the ColorDatatypeAdapter
with the decoder. The adapter is
registered for the XML schema type ColorType
and the java class Color
.
class CustomDecoderLibrary extends TLcdXMLDataObjectDecoderLibrary {
...
@Override
protected void doConfigure( TLcdXMLSchemaBasedDecoder aDecoder ) {
super.doConfigure( aDecoder );
aDecoder.getTypeUnmarshallerProvider().registerDatatypeUnmarshaller(
CustomConstants.COLOR_TYPE_ID,
Color.class,
new ColorDatatypeAdapter() );
}
}
Finally, Program: A custom schema mapping shows how the data model is annotated with a TLcdXMLSchemaMappingAnnotation
with a new instance of the CustomDecoderLibrary
.
samples/xml/customdecodingencoding/CustomDecodingEncodingDataTypes
)
// associates the custom decoder and encoder libraries with the data model
dataModelBuilder.annotateFromFactory(new DataModelAnnotationFactory<TLcdXMLSchemaMappingAnnotation>() {
@Override
public TLcdXMLSchemaMappingAnnotation createAnnotation(TLcdDataModel aDataModel) {
return new TLcdXMLSchemaMappingAnnotation(
new MappingLibrary(aDataModel),
new DecoderLibrary(aDataModel),
new EncoderLibrary(aDataModel)
);
}
});
Note that this snippet uses the CustomMappingLibrary
. This library is not strictly necessary for a simple custom type. It is necessary in case of custom complex types as explained
in Custom complex types.
Custom complex types
Is it also possible to fully customize the mapping of complex XML schema types. The two main differences with customizing
simple types are that the TLcdXMLSchemaBasedMapping
should be properly configured and that the decoding process of complex types is typically a hierarchical process that uses
delegation to many other unmarshallers.
The main responsibility of the TLcdXMLSchemaBasedMapping
is to keep track of information that is common to both encoding and decoding. This includes information about XML schema
elements and types. It also manages the ILcdXMLObjectFactory
instances that are responsible for creating new instances for a certain XML schema type. Both the TLcdXMLSchemaBasedEncoder
and the TLcdXMLSchemaBasedDecoder
aggregate a TLcdXMLSchemaBasedMapping
instance.
To decode XML complex types, an instance of ILcdXMLTypeUnmarshaller
is used. This instance needs to be registered just like an ILcdXMLDatatypeUnmarshaller
on the TLcdXMLSchemaBasedDecoder
.
The xml.binding.custom
sample shows how this can be done in code. The sample defines the XML schema type PointType
as a complex type with two attributes (x
and y
). In the Java domain model, this PointType
is mapped on the TLcdLonLatPoint
class. This class does not implement ILcdDataObject
, so you need to map it as a primitive type. Program: Mapping a complex XML schema type on a primitive type shows how you can do this using a custom extension of TLcdXMLDataModelBuilder
. By overriding the buildType
method you can control how the data model type is defined for an XML schema type. In this case, the type is defined as a
primitive type with instance class ILcd2DEditablePoint
. Note that you need an extension because otherwise the type would be defined as a data type with two properties.
TLcdXMLDataModelBuilder builder = new TLcdXMLDataModelBuilder() {
@Override
protected void buildType( TLcdDataTypeBuilder aTypeBuilder, TLcdXMLSchemaTypeIdentifier aTypeId ) {
if ( aTypeBuilder.getName().equals( "PointType" ) ) {
aTypeBuilder.instanceClass( ILcd2DEditablePoint.class );
aTypeBuilder.primitive( true );
} else {
super.buildType( aTypeBuilder, aTypeId );
}
}
});
Program: A custom object factory shows how the MappingLibrary
class defines and configures the object factory for the PointType
. Note that this mapping library also registers the ILcd2DEditablePoint
interface with the TLcdXMLJavaClassResolver
of the mapping. Exporting classes using the TLcdXMLJavaClassResolver explains in more detail why you need to do this.
samples/xml/customdecodingencoding/MappingLibrary
)
class MappingLibrary extends TLcdXMLDataObjectMappingLibrary {
public MappingLibrary(TLcdDataModel aDataModel) {
super(aDataModel);
}
@Override
protected void doConfigureMapping(TLcdXMLSchemaBasedMapping aMapping) {
super.doConfigureMapping(aMapping);
aMapping.getTypeObjectFactoryProvider().registerTypeObjectFactory(
CustomDecodingEncodingConstants.POINT_TYPE_ID,
ILcd2DEditablePoint.class, new ILcdXMLObjectFactory<ILcd2DEditablePoint>() {
@Override
public ILcd2DEditablePoint createObject(ILcdXMLDocumentContext aContext) {
return new TLcdLonLatPoint();
}
@Override
public ILcd2DEditablePoint resolveObject(ILcd2DEditablePoint aObject, ILcdXMLDocumentContext aContext) {
return aObject;
}
}
);
// register the interface
List<Class<?>> interfaces = new ArrayList<Class<?>>();
interfaces.add(ILcd2DEditablePoint.class);
aMapping.getJavaClassResolver().registerClassPriorityList(interfaces);
}
}
Program: Custom schema type unmarshalling shows how the ILcdXMLTypeUnmarshaller
for the PointType
moves the point to the location determined by the x
and y
attributes. Note that the type marshaller does not need to create a new instance; this is already done by the object factory.
samples/xml/customdecodingencoding/PointTypeAdapter
)
@Override
public ILcd2DEditablePoint unmarshalType(ILcd2DEditablePoint aObject,
XMLStreamReader aReader, ILcdXMLDocumentContext aContext)
throws XMLStreamException {
String x = aReader.getAttributeValue(CustomDecodingEncodingConstants.NAMESPACE_URI, "x");
String y = aReader.getAttributeValue(CustomDecodingEncodingConstants.NAMESPACE_URI, "y");
aObject.move2D(Double.parseDouble(x), Double.parseDouble(y));
aReader.nextTag();
return aObject;
}
Advanced type unmarshalling
In general, writing an ILcdXMLTypeUnmarshaller
can be much more complex than shown in the examples of the previous section. Fortunately, the XML framework is designed to
help you as much as possible with this as described in this section.
Unmarshalling contract
The core interface of schema type unmarshalling is ILcdXMLTypeUnmarshaller
. The contract of this interface defines how a schema type is unmarshalled into a Java model object. The interface defines
a single unmarshalType
method. The responsibility of this method is to unmarshal the attributes and content defined in a given XML type.
At the moment this method is called, the XMLStreamReader
is positioned at the start tag of the element to be unmarshalled. The type of this element is the given XML type or an extension
of that type.
When this method returns, the cursor of the XMLStreamReader
should be left at the start tag of the first child element that cannot be handled by this unmarshaller. Or, if no such element
exists, at the end tag of the element to be unmarshalled. The former typically happens in case the element is of a derived
type that has been extended with additional elements.
The object into which the contents of the XML element should be unmarshalled is passed as an argument to the method. Only
in case this argument is null, the method should create a new Java object. This typically happens when there is no appropriate
ILcdXMLObjectFactory
registered or if the factory cannot create a new instance.
Dealing with type extension
The ILcdXMLTypeUnmarshaller
interface supports extension by allowing to chain a set of schema type unmarshallers corresponding to the XML Schema type
hierarchy. In this chain, each unmarshaller is responsible for unmarshalling only those attributes and child elements which
are declared in the corresponding type.
In order to set up such a chain, the TLcdXMLSchemaBasedDecoder
provides a TLcdXMLTypeUnmarshallerProvider
that you can use to find an appropriate schema type unmarshaller for a given schema type and Java class. This provider is
also used to find an appropriate parent type unmarshaller.
When a type unmarshaller needs to unmarshal an object, it first unmarshals its own declared attributes, then optionally delegates to its parent and finally unmarshals its own declared elements and content. This approach is consistent with XSD type extension since XML does not define an order on attribute and since elements of extended types always appear after the elements of the super types. Note that in order for this chaining to work, the type unmarshaller should not consume the start element and end element event. These events are handled automatically by the framework.
The |
To be able to reuse common functionality, the xml.bind.custom
sample introduces a common ancestor class, AbstractSchemaTypeAdapter
. This class structures the (un)marshalling code and provides some utility methods to, for example, (un)marshal child elements,
skip whitespace, and more. Program: Type unmarshalling shows how the basic contract of the ILcdXMLTypeUnmarshaller
interface is implemented. First, if no instance is passed as argument, a new instance that represents the current XML element
in the model, is created. Then the attributes declared in this type are processed. If the type has a base type, then the base
type’s unmarshaller is called. Finally, the type unmarshaller unmarshals its declared content and the new instance is returned.
The createNewInstance
, unmarshalDeclaredAttributes
and unmarshalDeclaredContent
are placeholder methods that need to be overridden by subclasses.
samples/xml/customdecodingencoding/AbstractSchemaTypeAdapter
)
@Override
public T unmarshalType(T aObject, XMLStreamReader aReader,
ILcdXMLDocumentContext aContext) throws XMLStreamException {
if (aObject == null) {
aObject = createNewInstance();
}
unmarshalDeclaredAttributes(aObject, aReader, aContext);
ILcdXMLTypeUnmarshaller<? super T> parentMarshaller = getParentUnmarshaller();
if (parentMarshaller != null) {
parentMarshaller.unmarshalType(aObject, aReader, aContext);
} else {
aReader.next(); // consume start element
}
unmarshalDeclaredContent(aObject, aReader, aContext);
return aObject;
}
private ILcdXMLTypeUnmarshaller<? super T> getParentUnmarshaller() {
if (fParentID == null) {
return null;
}
return getSchemaDecoder().getTypeUnmarshallerProvider().getTypeUnmarshaller(fParentID, fParentClass, true);
}
Child elements
When a type unmarshaller needs to unmarshal child elements, it looks up an appropriate unmarshaller for its children using
the TLcdXMLSchemaBasedUnmarshallerProvider
of the TLcdXMLSchemaBasedDecoder
.
At this point, you need to know how the child element is defined. In XML schema, child elements can either be locally defined
or be references to global elements. An important difference between local and global elements is that XML Schema allows substitution
of global elements by other elements. This means that a document can use another element, instead of the declared global element,
without becoming invalid. An important constraint here is that XML schema only allows elements that are in the global element’s
substitution group
as replacement elements. Substitution groups are explicitly defined in the XML schema. Only elements of the same type or
of an extension of the type of the substituted element can be part of a substitution group. As such, you can use substitution
groups to model polymorph relations.
The important consequence for the type unmarshaller is that only for locally defined elements the XML schema type is known
in advance. Because of potential substitution of global elements, you cannot determine the type of the element based on the
schema alone.
Note that, next to substitution groups, XML defines a second mechanism called xsi:type
that enables the use of extension types in a document. This mechanism is currently not supported by the LuciadLightspeed
XML framework.
To unmarshal a local element, the type unmarshaller should look up an appropriate ILcdXMLUnmarshaller
based on the schema element identifier and the required Java class. Program: Finding an unmarshaller for a local element shows how the xml.customdecodingencoding
sample does this. The returned ILcdXMLUnmarshaller
is then used to unmarshal the local element into a Java object of the given class.
samples/xml/customdecodingencoding/AbstractSchemaTypeAdapter
)
protected <U> ILcdXMLUnmarshaller<? extends U> getUnmarshaller(TLcdXMLSchemaElementIdentifier aSchemaElement,
Class<U> aClass) {
return getSchemaDecoder().getUnmarshallerProvider().getUnmarshaller(aSchemaElement, aClass);
}
To unmarshal a global element, the type unmarshaller should ask the framework for an appropriate unmarshaller based on the
current element name and the required Java class. The TLcdXMLUnmarshallerProvider
of the TLcdXMLSchemaBasedDecoder
consults the registered information about substitution groups to find an appropriate result. The advantage of this approach
is that the parent unmarshaller does not have to know anything about the substitutes of the child element. As a consequence,
extending the substitution group with a new element can simply be done by registering the unmarshaller for the new element
on the unmarshaller provider, without having to modify the parent type’s unmarshaller code. Program: Unmarshalling a global child element shows how this is implemented in the xml.customdecodingencoding
sample.
samples/xml/customdecodingencoding/AbstractSchemaTypeAdapter
)
protected <U> U unmarshalChild(XMLStreamReader aReader, Class<U> aClass, ILcdXMLDocumentContext aContext) throws XMLStreamException {
ILcdXMLUnmarshaller<? extends U> unmarshaller = getSchemaDecoder().getUnmarshallerProvider().
getUnmarshaller(aReader.getName(), aClass);
U result = unmarshaller.unmarshal(aReader, aContext);
return result;
}
An example
The xml.binding.custom
sample provides an example of a more complex custom type unmarshaller for the ExtendedAddressType
XML schema type. Program: Definition of the ExtendedAddressType
shows how this type extends the AddressType
with a local and a global element.
ExtendedAddressType
<xsd:complexType name="ExtendedAddressType">
<xsd:complexContent>
<xsd:extension base="base:AddressType">
<xsd:sequence>
<xsd:element ref="tns:location" />
<xsd:element name="color" type="tns:ColorType" />
</xsd:sequence>
</xsd:extension>
</xsd:complexContent>
</xsd:complexType>
Program: Unmarshalling child elements shows the type unmarshaller for the ExtendedAddressType
. It is designed such that it can also correctly decode elements where the location
and the color
child elements are switched. When the reader’s current name equals color
, a color object is decoded. This is done using the color unmarshaller for the local color
element. Note that this unmarshaller can be cached in the adapter because the type of the color
element is fixed from the XML schema. When the reader’s current name is in the substitution group of the global location
element, the location is unmarshalled based on the current name and the ILcd2DEditablePoint
class. When an unknown element is encountered, an exception is thrown.
samples/xml/customdecodingencoding/ExtendedAddressTypeAdapter
)
protected void unmarshalDeclaredContent(ExtendedAddress aResult, XMLStreamReader aReader,
ILcdXMLDocumentContext aContext) throws XMLStreamException {
skipAllWhiteSpace(aReader);
while (aReader.isStartElement()) {
// expect either location or color
if (aReader.getName().equals(CustomDecodingEncodingConstants.COLOR_ELEMENT_ID.getElementNames()[0])) {
aResult.setColor(getColorUnmarshaller().unmarshal(aReader, aContext));
} else if (getSchemaDecoder().getMapping().getSchemaSet().isSubstitutableBy(CustomDecodingEncodingConstants.LOCATION_ELEMENT_ID.getElementName(), aReader.getName())) {
aResult.setLocation(unmarshalChild(aReader, ILcd2DEditablePoint.class, aContext));
} else {
throw new XMLStreamException("Unexpected element", aReader.getLocation());
}
aReader.nextTag();
}
}
private ILcdXMLUnmarshaller<? extends Color> getColorUnmarshaller() {
if (fColorUnmarshaller == null) {
fColorUnmarshaller = getUnmarshaller(CustomDecodingEncodingConstants.COLOR_ELEMENT_ID, Color.class);
}
return fColorUnmarshaller;
}
Exporting classes using the TLcdXMLJavaClassResolver
In a typical XML-to-Java binding, there is a one-on-one mapping between XML elements and Java classes. In the XML binding framework, this means that a marshaller is normally registered for a specific XML element and specific Java class. However, there are cases in which this mechanism is not powerful enough:
-
Users of a Java domain model might want to extend a class to add some functionality, but map the extension class on the same XML element as its super class. This would require to register the marshaller for all extension classes as well, which is not always possible (it might be unknown at the moment an XML library is written which extensions will be made in the future).
-
Users might want to bind an XML element to a Java interface, instead of a class. This becomes complex however when a class implements multiple interfaces: to which interface should the framework bind the class to?
The TLcdXMLSchemaBasedMapping
has a TLcdXMLJavaClassResolver
which provides functionality for mapping a Java class to another class or an interface. Whenever no marshaller is found for
the class to be marshalled, this resolver makes a list of all Java classes/interfaces that this class extends or implements,
and choose one of them to bind against, based on a priority list. This allows to register marshallers on the marshaller provider
for super classes or interfaces only, and let the class resolver take care of the mapping of derived classes to one of these
base classes/interfaces.
Runtime schema extension
The XML framework also provides support for runtime schema extension. This allows decoding of XML documents of which the XML schema is not known in advance. A particular interesting case here is the situation where a subset of the schemas used in a document is known in advance, as is the case with, for example, GML. The standardized GML schema defines the core geographic information concepts and is abstract. Applications that want to express content in GML need to define an extension to GML in a specialized application schema. Such an application schema defines the concepts that are specific to that application making use of and possibly extending from existing GML types and elements. The support for runtime schema extensions then makes it possible to decode documents of application schemas using the default GML decoder.
In order to support runtime schema extension, the Java model classes for which the types can be extended by extension schemas,
need to implement ILcdDataObject
.
To enable schema extension, a decoder needs to be created with a TLcdXMLDataObjectSchemaHandler
. This handler reads all unknown schemas encountered. For all these schemas, it creates a new data model and configures the
decoder(encoder) with it. This allows the decoder to handle these schemas as well.