Introduction

Usually when parsing a schema document, developers will prefer to make custom objects for attribute and element extensions. XMLSchema supports this through an extension registry mechanism. This document explains how this extension mechanism works by going through two complete examples. These two examples are included as test cases with the source release.

Example Extension

Following are two example schema documents that contain external attributes/elements

This schema demonstrates the use of an extension attribute

                     <schema
                        xmlns="http://www.w3.org/2001/XMLSchema"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                        xmlns:tns="http://soapinterop.org/types"
                        xmlns:ext="http://customattrib.org"
                        targetNamespace="http://soapinterop.org/types">

                    <complexType name="Struct" ext:customAttrib="toplevel:type">
                        <sequence>
                            <element name="varString" type="xsd:string" ext:customAttrib="inner:element"/>
                            <element name="varInt" type="xsd:int" ext:customAttrib="inner:element"/>
                            <element name="varFloat" type="xsd:float" ext:customAttrib="inner:element"/>
                            <element name="varStruct" type="tns:Struct" ext:customAttrib="inner:element"/>
                        </sequence>
                    </complexType>

                    <element name="attrTest" type="tns:Struct" ext:customAttrib="toplevel:element"/>

                </schema>

            

This schema demonstrates the use of an extension element

                           <schema
                    xmlns="http://www.w3.org/2001/XMLSchema"
                    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                    xmlns:tns="http://soapinterop.org/types"
                    xmlns:ext="http://customattrib.org"
                    targetNamespace="http://soapinterop.org/types">

                <complexType name="Struct">
                    <ext:customElt prefix="ext" suffix="type"/>
                    <sequence>
                        <element name="varString" type="xsd:string"/>
                        <element name="varInt" type="xsd:int" />
                        <element name="varFloat" type="xsd:float" />
                        <element name="varStruct" type="tns:Struct" />
                    </sequence>
                </complexType>

                <element name="attrTest" type="tns:Struct" >
                    <ext:customElt prefix="ext" suffix="elt"/>
                </element>

            </schema>
            

A developer would like the parser to make custom objects when it encounters customAttrib or customElt. This can be achieved by writing a custom extension deserializer and serializer and registering them with the extension registry. The next section briefly explains the important classes involved in this process.

Important classes

Following are the important classes in writing an extension

  • org.apache.ws.commons.schema.extensions.ExtensionRegistry
  • org.apache.ws.commons.schema.extensions.ExtensionDeserializer
  • org.apache.ws.commons.schema.extensions.ExtensionSerializer

Last two classes are interfaces that should be implemented by the respective implementations.

Code for a Deserializer

The deserializer needs to implement the org.apache.ws.commons.schema.extensions.ExtensionDeserializer interface. Following is a code fragment of the Extension deserializer that deserializes the extension attribute into a custom object. Note that once the custom object is made it is attached to the meta info map of the relevant XMLSchema object with the QName as the key

                 
                 public void deserialize(XmlSchemaObject schemaObject, QName name, Node domNode) {
                 if (CustomAttribute.CUSTOM_ATTRIBUTE_QNAME.equals(name)){
                     Attr attrib = (Attr)domNode;
                     String value = attrib.getValue();
                     //break the attrib into
                     CustomAttribute customAttrib = new CustomAttribute();
                     String[] strings = value.split(":");
                     customAttrib.setPrefix(strings[0]);
                     customAttrib.setSuffix(strings[1]);

                     //put this in the schema object meta info map
                     schemaObject.addMetaInfo(CustomAttribute.CUSTOM_ATTRIBUTE_QNAME,customAttrib);
                  }
                 }
                 
             

Note that prior knowledge is required about the format of the string of the attribute value. The complete custom attribute deserializer is available in tests.customext.attrib.CustomAttributeDeserializer

Following is a code fragment of the Extension deserializer that deserializes the extension element into a custom object. This is similar to the attribute case but now the node passed is the actual extension element itself.

                 
                  public void deserialize(XmlSchemaObject schemaObject, QName name, Node domNode) {
                     if (CustomElement.CUSTOM_ELT_QNAME.equals(name)){
                         Element elt = (Element)domNode;

                         CustomElement customElement = new CustomElement();
                         customElement.setPrefix(elt.getAttribute("prefix"));
                         customElement.setSuffix(elt.getAttribute("suffix"));

                         //put this in the schema object meta info map
                         schemaObject.addMetaInfo(CustomElement.CUSTOM_ELT_QNAME,customElement);
                     }
    }
                 
             

The complete custom attribute deserializer is available in tests.customext.elt.CustomElementDeserializer

Code for a Serializer

The serializer needs to implement the org.apache.ws.commons.schema.extensions.ExtensionSerializer interface. Following is a code fragment of the Extension serializer that serializes a given custom object into an attributeObject. Note that XMLSchema serialization mechanism is to create a DOM tree and serialize it. Hence the custom serializers needs to create the appropriate DOM node and attach it.

                 
                 public void serialize(XmlSchemaObject schemaObject, Class classOfType, Node domNode) {
                        Map metaInfoMap = schemaObject.getMetaInfoMap();
                        CustomAttribute att = (CustomAttribute)metaInfoMap.get(CustomAttribute.CUSTOM_ATTRIBUTE_QNAME);

                        Element elt = (Element)domNode;
                        Attr att1 = elt.getOwnerDocument().createAttributeNS(CustomAttribute.CUSTOM_ATTRIBUTE_QNAME.getNamespaceURI(),
                                                                             CustomAttribute.CUSTOM_ATTRIBUTE_QNAME.getLocalPart());
                        att1.setValue(att.getPrefix() + ":" + att.getSuffix());
                        elt.setAttributeNodeNS(att1);
                    }
                 
             

Note that prior knowledge is required about the format of the string of the attribute value. DomNode passed in would be the parent node of the serialized DOM tree.The complete custom attribute serializer is available in tests.customext.attrib.CustomAttributeSerializer

Following is a code fragment of the Extension serializer that serializes the custom object into an extension element. This is similar to the attribute case.

                 
                   public void serialize(XmlSchemaObject schemaObject, Class classOfType, Node domNode) {
                       Map metaInfoMap = schemaObject.getMetaInfoMap();
                       CustomElement customElt = (CustomElement)metaInfoMap.get(CustomElement.CUSTOM_ELT_QNAME);

                        Element elt = (Element)domNode;
                        Element extElt = elt.getOwnerDocument().createElementNS(CustomElement.CUSTOM_ELT_QNAME.getNamespaceURI(),
                                                                             CustomElement.CUSTOM_ELT_QNAME.getLocalPart());
                        extElt.setAttribute("prefix",customElt.getPrefix());
                        extElt.setAttribute("suffix",customElt.getSuffix());

                        elt.appendChild(extElt);

                    }
                 
             

The complete custom element serializer is available in tests.customext.elt.CustomElementSerializer

Registering Types and Using a Custom Extension Registry

Once the serilizers are made they need to be registered with the registry. This can be done by the following way.

                
                   //register our custom type
                    registerDeserializer(CustomElement.CUSTOM_ELT_QNAME,new CustomElementDeserializer());
                    registerSerializer(CustomElement.class,new CustomElementSerializer());
                
            

It can be a more convenient if the extension types are registered inside a subclass of the org.apache.ws.commons.schema.extensions.ExtensionRegistry to avoid any confusions.

If the system property"org.apache.ws.commons.extensions.ExtensionRegistry" is present the extension registry will be instantiated with the class specified. Following is an example of how this can be done

                   
                       System.setProperty(Constants.SystemConstants.EXTENSION_REGISTRY_KEY,
                                    CustomExtensionRegistry.class.getName());
                   
               

This behavior is quite useful when the XMlSchema object models are used internally with no direct access to the extension registry.

Conclusion

The extension mechanism of the XMLSchema object model allows the users to add their custom objects to the standard XMLSchema objects.