2007/09/24
24 Sep, 2007

ADB XML Schema Support

  • Amila Suriarachchi
  • Architect, Member, Management Committee - Data Technologies - WSO2

Background

adbADB supports most of the common XML schema types used in Web services. These common data types are described in the Web service data binding working group [2]. Although ADB does not support 100% of these schema constructs, it supports more than 85% percent of it with the Axis2 1.3 release. Therefore, this article mainly focuses on these supported schema constructs with the sample schema and generated code.

Simple Type Support

ADB supports all restriction, union, and list types since the Axis2 1.2 release.

Simple type restriction

A basic simple type looks like this.

<xs:simpleType name="TestSimpleType">
<xs:restriction base="xs:string"/>
</xs:simpleType>

The generated code for this simple type has a local variable of the type string.

protected java.lang.String localTestSimpleType;

public java.lang.String getTestSimpleType() {
return localTestSimpleType;
}

public void setTestSimpleType(java.lang.String param) {
this.localTestSimpleType = param;
}

The above restriction can have constraints like pattern, maxLength, minLength, maxInclusive, minInclusive, maxExclusive and minExclusive. For these constraints, validation code is generated in the setter method. Let's take the following simple type with a pattern constraint.

<xs:simpleType name="TestSimpleType1">
<xs:restriction base="xs:string">
<xs:pattern value="[A-Z]"/>
</xs:restriction>
</xs:simpleType>

Here is the generated setter method for the simple type. Similarly a validation code is generated for other constraints as well.

public void setTestSimpleType1(java.lang.String param) {
if (java.lang.String.valueOf(param).matches("[A-Z]")) {
this.localTestSimpleType1 = param;
} else {
throw new java.lang.RuntimeException();
}
}

The restriction type enumeration is a special type. For this type a separate object is created for each enumeration value. Consider the following schema with enumeration restriction.

<xs:simpleType name="TestSimpleType2">
<xs:restriction base="xs:string">
<xs:enumeration value="value1"/>
<xs:enumeration value="value2"/>
<xs:enumeration value="value3"/>
</xs:restriction>
</xs:simpleType>

The generated class for this simple type has constants for each value so that each value can be accessed using these constants. (e.g. TestSimpleType2.value1, TestSimpleType2.value2)

public static final TestSimpleType2 value1 = new TestSimpleType2(_value1,true);
public static final TestSimpleType2 value2 = new TestSimpleType2(_value2,true);
public static final TestSimpleType2 value3 = new TestSimpleType2(_value3,true);

Simple type union

Simple type union is used to specify the possible simple types for an element. Therefore at runtime, the parser has to decide the type of the Java variable representing the element. When generating the code, the element variable type is set as java.lang.Object At runtime, the ADB parser determines the correct type using the xsi:type (the xsi prefix is used to represent the XML instance namespace [3]) attribute of the corresponding XML element. Consider the simple type given below.

<xs:simpleType name="TestSimpleType3">
<xs:union memberTypes="xs:int xs:string"/>
</xs:simpleType>

Here is the generated setter method. It checks whether the given object is in the correct object type.

public void setObject(java.lang.Object object) {
if (object instanceof java.lang.Integer) {
this.localObject = object;
} else if (object instanceof java.lang.String) {
this.localObject = object;
} else {
throw new RuntimeException("Invalid object type");
}
}

The serialize method checks the object type and sets the xsi:type attribute accordingly.

if (localObject instanceof java.lang.Integer) {
java.lang.String namespacePrefix = registerPrefix(xmlWriter, "https://www.w3.org/2001/XMLSchema");
if ((namespacePrefix != null) && (namespacePrefix.trim().length() > 0)) {
writeAttribute("xsi", "https://www.w3.org/2001/XMLSchema-instance", "type",
namespacePrefix + ":int",
xmlWriter);
} else {
writeAttribute("xsi", "https://www.w3.org/2001/XMLSchema-instance", "type",
"int",
xmlWriter);
}
xmlWriter.writeCharacters(localObject.toString());
} else if (localObject instanceof java.lang.String) {
java.lang.String namespacePrefix = registerPrefix(xmlWriter, "https://www.w3.org/2001/XMLSchema");
if ((namespacePrefix != null) && (namespacePrefix.trim().length() > 0)) {
writeAttribute("xsi", "https://www.w3.org/2001/XMLSchema-instance", "type",
namespacePrefix + ":string",
xmlWriter);
} else {
writeAttribute("xsi", "https://www.w3.org/2001/XMLSchema-instance", "type",
"string",
xmlWriter);
}
xmlWriter.writeCharacters(localObject.toString());
} else {
throw new org.apache.axis2.databinding.ADBException("Invalid object type");
}

All the classes for the union type is extended from the org.apache.axis2.databinding.types.Union class. This class has a method called setObject which is used to determine the object type and set the correct Object for the basic XML types. The generated fromString method which is used to create the object from the xmlstream reader would look like this.

public static TestSimpleType3 fromString(javax.xml.stream.XMLStreamReader xmlStreamReader,
java.lang.String namespaceURI,
java.lang.String type)
throws org.apache.axis2.databinding.ADBException {
TestSimpleType3 object = null;
try {
if ("https://www.w3.org/2001/XMLSchema".equals(namespaceURI)) {
object = new TestSimpleType3();
object.setObject(xmlStreamReader, namespaceURI, type);
} else {
object = new TestSimpleType3();
object.setObject(org.tempuri.boolean1.ExtensionMapper
.getTypeObject(namespaceURI, type, xmlStreamReader));
}
return object;
} catch (java.lang.Exception e) {
throw new org.apache.axis2.databinding.ADBException("Error in parsing value");
}
}

At the fromString method, first it checks for the XML schema namespace. If the type is not a standard, the XML schema type ADB parser uses the Extension mapper to get the correct object.

Simple type list

The simple type list is used to specify a space separated list of items. Here the item type has to be specified and it can be any simple type.

<xs:simpleType name="TestSimpleType4">
<xs:list itemType="xs:string"></xs:list>
</xs:simpleType>

The generated class uses an array to keep the item list and concatenates them in the toString method before serializing.

protected java.lang.String[] localString;

public java.lang.String[] getString() {
return localString;
}

public void setString(java.lang.String[] itemList) {
this.localString = itemList;
}

public java.lang.String toString() {
java.lang.StringBuffer outString = new java.lang.StringBuffer();
if (localString != null) {
for (int i = 0; i < localString.length; i++) {
outString.append(localString[i].toString()).append(" ");
}
}
return outString.toString().trim();
}

Complex Type Support

ADB provides support for some advanced complex type constructs apart from the basic sequence, choice and all support.

MinOccurs and MaxOccurs support at Sequence and Choice elements

The XML Schema defines many schema constructs to let people define XML documents in anyway they prefer. Specifying MaxOccurs at the sequence level lets users repeat the sequence element block as many times as they like. Consider the following complex type.

<xs:complexType name="TestComplexType1">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="param1" type="xs:string"/>
<xs:element name="param2" type="xs:string"/>
</xs:sequence>
</xs:complexType>

There are two classes created for this complex type. The class TestComplexType1Sequence is created to keep the details for one sequence block. The class TestComplexType1 is actually created for this type and is used to keep an array of a former class, enabling it to keep an array of sequence blocks.

Inner sequence and choice element support

Sequence and choice can also be defined within those elements. This would let people group elements in a better way than defining all the elements in one block. Here is a sample of such a schema part.

<xs:complexType name="TestComplexType2">
<xs:sequence>
<xs:element name="param1" type="xs:string"/>
<xs:element name="param2" type="xs:string"/>
<xs:choice>
<xs:element name="param3" type="xs:string"/>
<xs:element name="param4" type="xs:string"/>
</xs:choice>
</xs:sequence>
</xs:complexType>

There is a special class generated for the inner choice called TestComplexType2Choice_type0. The TestComplexType2 class which is generated for this complex type contains the param1, param2 and another variable of the TestComplexType2Choice_type0 type to keep the details of the choice block.

Complex type extension

Java allows users to use inheritance through extension. Similarly the XML schema also provides inheritance through the extension. Here are two complex types; one extends the other.

<xs:complexType name="TestComplexType3">
<xs:sequence>
<xs:element name="param1" type="xs:string"/>
<xs:element name="param2" type="xs:string"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="TestComplexType4">
<xs:complexContent>
<xs:extension base="tns:TestComplexType3">
<xs:sequence>
<xs:element name="param3" type="xs:string"/>
<xs:element name="param4" type="xs:string"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>

The TestComplexType4 class, which is for the extended schema type, has extended from the TestComplexType3 which is for the parent schema type. The serialize method of any extended class writes its element type using the type attribute. The following code fragment does that for the TestComplexType4.

java.lang.String namespacePrefix = registerPrefix(xmlWriter, "https://tempuri.org/boolean1");
if ((namespacePrefix != null) && (namespacePrefix.trim().length() > 0)) {
writeAttribute("xsi", "https://www.w3.org/2001/XMLSchema-instance", "type",
namespacePrefix + ":TestComplexType4",
xmlWriter);
} else {
writeAttribute("xsi", "https://www.w3.org/2001/XMLSchema-instance", "type",
"TestComplexType4",
xmlWriter);
}

When creating the object structure from the XML stream, the ADB parser always checks the type attribute. If it can find a type attribute, the ADB parser sends the XML stream to the Extension Mapper to find out the correct object type. This is the mechanism that ADB uses to support polymorphism.

if (reader.getAttributeValue("https://www.w3.org/2001/XMLSchema-instance", "type") != null) {
java.lang.String fullTypeName = reader.getAttributeValue("https://www.w3.org/2001/XMLSchema-instance",
"type");
if (fullTypeName != null) {
java.lang.String nsPrefix = null;
if (fullTypeName.indexOf(":") > -1) {
nsPrefix = fullTypeName.substring(0, fullTypeName.indexOf(":"));
}
nsPrefix = nsPrefix == null ? "" : nsPrefix;
java.lang.String type = fullTypeName.substring(fullTypeName.indexOf(":") + 1);
if (!"TestComplexType4".equals(type)) {
//find namespace for the prefix
java.lang.String nsUri = reader.getNamespaceContext().getNamespaceURI(nsPrefix);
return (TestComplexType4) org.tempuri.boolean1.ExtensionMapper.getTypeObject(
nsUri, type, reader);
}
}
}

XML Schema QName type support

The XML schema QName type is different from other simple types since it has two components.

A QName is a combination of a namesapce and a localpart. Consider the following complex type with the param1 element having a QName type.

<xs:complexType name="TestComplexType5">
<xs:sequence>
<xs:element name="param1" type="xs:QName"/>
</xs:sequence>
</xs:complexType>

When serializing the QName, the correct namespace prefix must be found for the QName and serialize it accordingly. ADB serializer uses the writeQName method for this purpose. On the other hand, when parsing the QName, the first namespace for the QName must be found using the prefix. Then this prefix can be used to find the correct namespace from the parser.

Handling XML Null Values with XML Primitive Types

Some of the XML simple types map to Java primitive types (e.g. int, boolean, double). Since Java primitive types cannot have null values, there is no way to handle nillable attributes with these types. For example, let's take the following complex type.

<xs:complexType name="TestComplexType6">
<xs:sequence>
<xs:element nillable="true" name="param1" type="xs:int"/>
</xs:sequence>
</xs:complexType>

The generated TestComplexType6 class has a field of the type int for param1 element. The param1 element is a nillable element. But Java primitive types cannot support null vaules. The solution for this problem provided by ADB is to consider one int value that represents the null. The value it uses is the Integer.MIN_VALUE. In the serialize method, it checks for this value and serializes accordingly.

if (localParam1 == java.lang.Integer.MIN_VALUE) {
writeAttribute("xsi", "https://www.w3.org/2001/XMLSchema-instance",
"nil", "1", xmlWriter);
} else {
xmlWriter.writeCharacters(org.apache.axis2.databinding.utils.
ConverterUtil.convertToString(localParam1));
}

At the parse method, if the XML element is null, then it sets the above value.

if (reader.getAttributeValue("https://www.w3.org/2001/XMLSchema-instance", "type") != null) {
java.lang.String fullTypeName = reader.getAttributeValue("https://www.w3.org/2001/XMLSchema-instance",
"type");
if (fullTypeName != null) {
java.lang.String nsPrefix = null;
if (fullTypeName.indexOf(":") > -1) {
nsPrefix = fullTypeName.substring(0, fullTypeName.indexOf(":"));
}
nsPrefix = nsPrefix == null ? "" : nsPrefix;
java.lang.String type = fullTypeName.substring(fullTypeName.indexOf(":") + 1);
if (!"TestComplexType6".equals(type)) {
//find namespace for the prefix
java.lang.String nsUri = reader.getNamespaceContext().getNamespaceURI(nsPrefix);
return (TestComplexType6) org.tempuri.boolean1.ExtensionMapper.getTypeObject(
nsUri, type, reader);
}
}
}

Similarly, the other types long, byte, double, float, short uses the Long.MIN_VALUE, Byte.MIN_VALUE, Double.NaN, Float.NaN, and Short.MIN_VALUE.

References

[1]ADB Generated Code in Apache Axis2

[2]https://www.w3.org/2002/ws/databinding/

[3]https://www.w3.org/2001/XMLSchema-instance

[4]https://wso2.org/library/236

Author

Amila Suriarachchi, Senior Software Engineer, WSO2 Inc. amila at wso2 dot com

 

 

About Author

  • Amila Suriarachchi
  • Architect, Member, Management Committee - Data Technologies
  • WSO2 Inc.