Processing XML schemas with XSLT for WebSphere DataPower SOA Appliances

XML schemas are a fundamental part of any XML-based SOA. Schemas can act as service contracts between service providers and service consumers, providing machine-enforceable sets of rules that describe the interface. This article shows you how to process multiple XSD documents using XSLT V1.0 stylesheets for WebSphere DataPower SOA Appliances.

Ramkumar Ramalingam, Software Developer, IBM  

Ramkumar RamalingamRamkumar Ramalingam is an Advisory Software Engineer at the IBM India Software Lab in Bangalore, India. He is a Committer and Management Committee member on the Apache Tuscany project, and a member of the OASIS SCA Java Specification Community. You can contact Ramkumar at ramkumar_rj@in.ibm.com.


developerWorks Contributing author
        level

22 December 2010

Also available in Chinese

Introduction

XML Schema Definitions (XSD's) are usually complex -- closer to pseudocode than human-readable English. Multiple layers of abstraction also make the flow of schema processing hard to understand. Most of the time, you need a specially designed schema parser to understand the structure and rules specified in schema documents.

One powerful advantage that XML schemas have over Document Type Definitions (DTD's) is that they are defined as XML documents themselves, making them programmatically accessible to developers, who can add tremendous flexibility to system architectures. XML schemas can be stored along with other XML documents in XML architectures and data stores, and then manipulated, referenced, and styled using a growing number of XML tools such as XPath, XQuery, XInclude/XPointer, and Extensible Stylesheet Language (XSL).

This article describes some effective approaches when processing multiple XSD documents with Extensible Stylesheet Language Transformations (XSLT) for IBM® WebSphere® DataPower® SOA Appliances (hereafter called DataPower). The article focuses on how to deal with the complexity of processing multiple XSD documents using XSLT V1.0, the programming language for DataPower.

This article assumes some experience with XSD and XSLT.

About DataPower

As XML adoption within the enterprise increases, a growing number of slow-performing applications demonstrates that existing overtaxed software systems cannot support next-generation applications. Enterprises need a platform that addresses XML's performance, security, and management requirements head-on. Powered by unique purpose-built technology, DataPower provides a high-speed engine for transforming XML using XSLT V1.0 at wirespeed.

From a transformation perspective, DataPower provides a high-performance XSLT processing engine. Since DataPower is XML-centric, the custom programming model for DataPower is XSLT, which is a full turing-complete programming language. XSLT V1.0 and XPath 1.0 support (with some 2.0 support) is available.

About DataPower XSLT extensions

DataPower extension elements and functions catalog lists all of the XSLT extension elements and functions available to stylesheets that run on the appliance. To use these extensions, a stylesheet needs to tell the XSLT engine about the dp: XML namespace, and that namespace contains extension elements. It's also common to also ask the processor not to include that namespace in the stylesheet output. This set-up occurs in the <xsl:stylesheet> element at the topmost level of the stylesheet.

One such extension element is <dp:url-open/>, which helps to send data to or receive data from an arbitrary URL, using a specified protocol. While processing multiple XSD document using XSLT V1.0, the <dp:url-open/> element can help in reading the schemas that resides over the network via a URL. This element is capable of returning a node-set to XSLT for further processing.

Listing 1. Syntax for dp:url-open element
<dp:url-open
    target="url"
    response="xml | binaryNode | ignore | responsecode | 
    responsecode-ignore | savefile"
    resolve-mode="xml | ignore"
    base-uri-node="nodeSet"
    data-type="xml | ignore"
    http-headers="xpathExpression"
    content-type="contentType"
        ssl-proxy="sslProxyName"
    timeout="timerValue">
</dp:url-open>

For more details on the above syntax and to understand different protocols that are supported by this extension element see DataPower extension elements and functions catalog.

To use the DataPower extension elements and functions in your XSLT stylesheets, you need to declare the dp: XML namespace prefix, as shown in Listing 2:

Listing 2. Declaring the XML namespace prefix
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:dp="http://www.datapower.com/extensions"
    extension-element-prefixes="dp"
    exclude-result-prefixes="dp">
<xsl:output method="xml"/>
...
</xsl:stylesheet>

The dp: XML namespace prefix is declared, and referenced as an extension element prefix. Explicit dp: elements should be processed as extensions, not copied to the output, and the prefix should be excluded from the stylesheet's result.

Schema namespace design approaches

Before tackling the challenges of processing multiple schema documents, its important to understand the role of namespaces in designing the schemas. Namespaces are an important concept in XML schemas. A schema can be viewed as a collection or vocabulary of type definitions and element declarations whose names belong to a target namespace, which enable you to differentiate definitions and declarations from different vocabularies.

Except for no-namespace schemas, every XML xchema uses at least two namespaces -- targetNamespace and the XMLSchema (http://www.w3.org/2001/XMLSchema) namespace. When multiple schemas are required, there can be multiple targetNamespace defined, one for each schema, and they are commonly designed using one of three design approaches:

Homogeneous namespace design
Using the same targetNamespace for each schema.
Heterogeneous namespace design
Using different targetNamespace for each schema.
Chameleon namespace design
Giving no targetNamespace to supporting schemas. (The no-namespace supporting schemas will take on the targetNamespace of the main schema, like a chameleon taking on the coloration of its surroundings.)

For more details on these design approaches, see Multi-schema projects: Zero, one, or many namespaces? at xfront.com.

XSD processing

Extracting structure from a XSD document using XSL results in some interesting challenges. One obstacle in processing XSD documents with XSL is the extensive use of namespace prefixes within attribute values in XSD documents. These namespace prefixes are used to correlate elements, simpleType, complexType, groups, and attributeGroups. While XSL is adept at handling namespaces on both elements and attributes in the input document, its constructs for dealing with them inside attribute values and character data are somewhat weak.

The developerWorks article Processing WSDL documents with XSLT describes techniques to deal with such issues in both WSDL and non-WSDL documents. This article uses some of these techniques to tackle the namespace prefixes within attribute values.

Another major obstacle arises when multiple XSD documents need to be processed to deal with different schema namespace design approaches, where the targetNamespace count can range from zero to many. Especially in a heterogeneous schema namespace design, each schema comes with a different targetNamespace. The challenge is to correlate elements, simpleType, complexType, groups, and attributeGroups that are distributed across multiple schema files and defined with different targetNamespaces. Apart from the obstacles described above, XSL designed to process XSD documents must also consider that elements, simpleTypes, complexTypes, groups, and attributeGroups can be defined and referenced in multiple ways within a schema.

Merging multiple schema documents

In the remainder of this article, you will learn how to merge multiple schema documents designed with different schema namespace design approaches. When dealing with multiple schema documents using XSLT V1.0, it is impossible to have permanent references to each of these schema documents and use them on-demand to correlate between elements, simpleType, complexType, groups, and attributeGroups. Therefore it is a good idea to merge them into a single entity before processing them to correlate between the elements, simpleType, complexType, groups, and attributeGroups. Described below are some useful techniques for merging multiple schema documents and efficiently correlating the elements, simpleType, complexType, groups, and attributeGroups.

Technique 1: Homogeneous namespace design

In a homogeneous or chameleon schema namespace design, only one targetNamespace exists. Merging multiple schema documents designed with this approach is straightforward, as all the schemas are confined to the same targetNamespace, and the merged entity appears just like any other individual schema with one targetNamespace. Listing 3 shows sample schemas that conform to this design approach, and Listing 4 below shows how to deal with this design approach using XSL to generate the merged entity. You can download all the sample code related to Technique 1 at the bottom of the article.

Listing 3. Sample input schemas
Product.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           targetNamespace="http://www.company.org"
           elementFormDefault="qualified">
    
        <xs:complexType name="ProductType">
        <xs:sequence>
            <xs:element name="Type" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Person.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
            targetNamespace="http://www.company.org"
            elementFormDefault="qualified">
    
        <xs:complexType name="PersonType">
        <xs:sequence>
            <xs:element name="Name" type="xs:string"/>
            <xs:element name="SSN" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Company.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.company.org"
    elementFormDefault="qualified">
    
    <xs:include schemaLocation="Person.xsd"/>
    <xs:include schemaLocation="Product.xsd"/>
    
        <xs:element name="Company">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Person" type="PersonType" maxOccurs="unbounded"/>
                <xs:element name="Product" type="ProductType" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
Listing 4. Technique 1 XSL
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
        version="1.0">

    <xsl:strip-space elements="*"/>
    <xsl:output method="xml"/>

    <xsl:template match="xs:include" mode="copy-node">
        <xsl:variable name="document" select="document(@schemaLocation)"/>
        <xsl:apply-templates select="$document" mode="copy-node"/>
    </xsl:template>

    <xsl:template match="xs:element|xs:complexType|xs:simpleType|
        xs:group|xs:attributeGroup" mode="copy-node">
    <xsl:element name="{name()}" namespace="{namespace-uri()}">
            <xsl:for-each select="./@*">
                <xsl:attribute name="{name()}">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:for-each>
            <xsl:copy-of select="./node()"/>
        </xsl:element>
    </xsl:template>   

    <xsl:template match="xs:schema">
        <xsl:element name="{name()}">
            <xsl:for-each select="./@*">
                <xsl:attribute name="{name()}">
                    <xsl:value-of select="."/>
                </xsl:attribute>
            </xsl:for-each>
            <xsl:apply-templates mode="copy-node"/>
        </xsl:element>
    </xsl:template>

    <xsl:template match="text()" mode="copy-node"/>
</xsl:stylesheet>

Listing 5 shows a sample output generated using Technique 1 XSL when applied over the schema documents shown in Listing 3 above. The generated merged entity looks just like any other individual schema with one targetNamespace:

Listing 5. Technique 1 Sample Output
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.company.org" 
    elementFormDefault="qualified">

    <xs:complexType name="PersonType">
        <xs:sequence>
            <xs:element name="Name" type="xs:string"/>
            <xs:element name="SSN" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="ProductType">
        <xs:sequence>
            <xs:element name="Type" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>

    <xs:element name="Company">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Person" type="PersonType" maxOccurs="unbounded"/>
                <xs:element name="Product" type="ProductType" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>

Technique 2: Heterogeneous namespace design

In a heterogeneous schema namespace design, each schema comes with a different targetNamespace. Merging multiple schema documents designed with this approach has some interesting challenges while dealing with elements, simpleType, complexType, groups, and attributeGroups as they may have the same name but different targetNamepaces, so the merged entity must provide enough information about the targetNamespace for each defined elements, simpleTypes, complexTypes, and others.

Listing 6 shows sample schemas using this design approach and Listing 7 below shows a technique to deal with this design approach using XSL to generate the merged entity. Again, you can download the sample code for Technique 2 at the bottom of the article.

Listing 6. Sample input schemas
Product.xsd
<xs:schema targetNamespace="http://www.product.org"	   
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:pro="http://www.product.org">

    <xs:complexType name="ProductType">
        <xs:sequence>
           <xs:element ref="WebSphere"/>
        </xs:sequence>
    </xs:complexType>

    <xs:element name="WebSphere" type="WebSphereType"/>
    <xs:element name="DB2" substitutionGroup="WebSphere" type="DB2Type"/>
    <xs:element name="Lotus" substitutionGroup="WebSphere" type="LotusType"/>

    <xs:complexType name="DB2Type">
        <xs:sequence>
            <xs:element name="version" type="xs:string"/>
            <xs:element name="features" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="LotusType">
        <xs:sequence>
            <xs:element name="version" type="xs:string"/>
            <xs:element name="features" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
    <xs:complexType name="WebSphereType">
        <xs:sequence>
            <xs:element name="version" type="xs:string"/>
            <xs:element name="features" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Person.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.person.org"            
        xmlns:per="http://www.person.org">

    <xs:complexType name="PersonType">
        <xs:sequence>
            <xs:element name="Name" type="xs:string"/>
            <xs:element name="SSN" type="xs:string"/>
                <xs:element name="Address" type="per:AddressType" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>

    <xs:complexType name="AddressType">
        <xs:sequence>
            <xs:element name="Street" type="xs:string"/>
            <xs:element name="City" type="xs:string"/>
            <xs:element name="Country" type="xs:string"/>
        </xs:sequence>
    </xs:complexType>
</xs:schema>

Company.xsd
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    targetNamespace="http://www.company.org"
    xmlns:per="http://www.person.org"
    xmlns:pro="http://www.product.org">

    <xs:import namespace="http://www.person.org" schemaLocation="Person.xsd"/>
    <xs:import namespace="http://www.product.org" schemaLocation="Product.xsd"/>
    <xs:element name="Company">
        <xs:complexType>
            <xs:sequence>
                <xs:element name="Person" type="per:PersonType" maxOccurs="unbounded"/>
                <xs:element name="Product" type="pro:ProductType" maxOccurs="unbounded"/>
            </xs:sequence>
        </xs:complexType>
    </xs:element>
</xs:schema>
Listing 7. Technique 2 XSL
<xsl:stylesheet xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        version="1.0">

<xsl:import href="utils.xsl"/>
<xsl:strip-space elements="*"/>
<xsl:output method="xml"/>

<xsl:template match="xs:schema">
    <xsl:variable name="namespaceprefix">
        <xsl:call-template name="prefix-for-target-namespace"/>
    </xsl:variable>
    <xs:schema>
        <xsl:for-each select="./@*">
            <xsl:attribute name="{name()}">
            <xsl:value-of select="."/>
        </xsl:attribute>
      </xsl:for-each>	 
      <xsl:apply-templates mode="copy-node"/>
      <xsl:for-each select="./*">
            <xsl:if test="not(name()='xs:import') and not(name()='xs:include')">
                <xsl:element name="{name()}" namespace="{namespace-uri()}">
                    <xsl:for-each select="./@*">
                        <xsl:attribute name="{name()}">
                            <xsl:value-of select="."/>
                        </xsl:attribute>
                    </xsl:for-each>
                    <xsl:attribute name="targetNamespace">
                        <xsl:value-of select="ancestor::xs:schema/@targetNamespace"/>
                    </xsl:attribute>
                    <xsl:attribute name="targetNamespacePrefix">
                        <xsl:value-of select="$namespaceprefix"/>
                    </xsl:attribute>
                    <xsl:copy-of select="./node()"/>
                </xsl:element>
            </xsl:if>
        </xsl:for-each>
    </xs:schema>
</xsl:template>

<xsl:template match="xs:import" mode="copy-node">
    <xsl:variable name="document" select="document(@schemaLocation)"/>
    <xsl:apply-templates select="$document" mode="copy-node"/>
</xsl:template>

<xsl:template match="xs:schema" mode="copy-node">
    <xsl:variable name="namespaceprefix">
        <xsl:call-template name="prefix-for-target-namespace"/>
    </xsl:variable>
    <xsl:apply-templates mode="copy-node"/>
    <xsl:for-each select="./*">
        <xsl:if test="not(name()='xs:import') and not(name()='xs:include')">
            <xsl:element name="{name()}" namespace="{namespace-uri()}">
                <xsl:for-each select="./@*">
                    <xsl:attribute name="{name()}">
                        <xsl:value-of select="."/>
                    </xsl:attribute>
                </xsl:for-each>
                <xsl:attribute name="targetNamespace">
                    <xsl:value-of select="ancestor::xs:schema/@targetNamespace"/>
                </xsl:attribute>
                <xsl:attribute name="targetNamespacePrefix">
                    <xsl:value-of select="$namespaceprefix"/>
                </xsl:attribute>
                <xsl:copy-of select="./node()"/>
            </xsl:element>
        </xsl:if>
    </xsl:for-each>
</xsl:template>

<xsl:template match="text()" mode="copy-node"/>

</xsl:stylesheet>

Listing 8 shows output generated using Technique 1 XSL when applied over the schema documents shown in Listing 6 above. Output for schema elements such as xs:element and xs:complexType has been generated with a new attribute called targetNamespace and targetNamespacePrefix, which is basically used to differentiate between the XSD element definitions that are imported from other schemas (designed with different targetNamespace) within this merged entity:

Listing 8. Technique 2 sample output
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="http://www.company.org" 
        elementFormDefault="qualified">
<!-- copied from person.xsd -->
<xs:complexType name="PersonType" targetNamespace="http://www.person.org" 
        targetNamespacePrefix="per">
    <xs:sequence xmlns:per="http://www.person.org">
            <xs:element name="Name" type="xs:string"/>
            <xs:element name="SSN" type="xs:string"/>
        <xs:element name="Address" type="per:AddressType" maxOccurs="unbounded"/>
    </xs:sequence>
</xs:complexType>
<xs:complexType name="AddressType" targetNamespace="http://www.person.org" 
        targetNamespacePrefix="per">
    <xs:sequence xmlns:per="http://www.person.org">
        <xs:element name="Street" type="xs:string"/>
        <xs:element name="City" type="xs:string"/>
        <xs:element name="Country" type="xs:string"/>
    </xs:sequence>
</xs:complexType>
<!-- copied from product.xsd -->
<xs:complexType name="ProductType" targetNamespace="http://www.product.org" 
        targetNamespacePrefix="pro">
    <xs:sequence xmlns:pro="http://www.product.org">
        <xs:element ref="WebSphere"/>
    </xs:sequence>
</xs:complexType>
<xs:element name="WebSphere" type="WebSphereType" 
        targetNamespace="http://www.product.org" targetNamespacePrefix="pro"/>
<xs:element name="DB2" substitutionGroup="WebSphere" type="DB2Type" 
        targetNamespace="http://www.product.org" targetNamespacePrefix="pro"/>
<xs:element name="Lotus" substitutionGroup="WebSphere" type="LotusType" 
        targetNamespace="http://www.product.org" targetNamespacePrefix="pro"/>
<xs:complexType name="DB2Type" targetNamespace="http://www.product.org" 
        targetNamespacePrefix="pro">
    <xs:sequence xmlns:pro="http://www.product.org">
        <xs:element name="version" type="xs:string"/>
        <xs:element name="features" type="xs:string"/>
    </xs:sequence>
</xs:complexType>
<xs:complexType name="LotusType" targetNamespace="http://www.product.org" 
        targetNamespacePrefix="pro">
   <xs:sequence xmlns:pro="http://www.product.org">
        <xs:element name="version" type="xs:string"/>
        <xs:element name="features" type="xs:string"/>
   </xs:sequence>
</xs:complexType>
<xs:complexType name="WebSphereType" targetNamespace="http://www.product.org" 
        targetNamespacePrefix="pro">
    <xs:sequence xmlns:pro="http://www.product.org">
        <xs:element name="version" type="xs:string"/>
        <xs:element name="features" type="xs:string"/>
    </xs:sequence>
</xs:complexType>
<!-- copied from company.xsd -->
<xs:element name="Company" targetNamespace="http://www.company.org" 
        targetNamespacePrefix="com">
    <xs:complexType xmlns:com="http://www.company.org" 
            xmlns:per="http://www.person.org" 
                xmlns:pro="http://www.product.org">
        <xs:sequence>
            <xs:element name="Person" type="per:PersonType" maxOccurs="unbounded"/>
            <xs:element name="Product" type="pro:ProductType" maxOccurs="unbounded"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
</xs:schema>

Though the use of an additional attributes like targetNamespace and targetNamespacePrefix for a XSD element definitions may not be allowed by the XML schema specification, this approach is useful in representing XSD element definitions from multiple schema documents designed with different targetNamespace into one single document or entity. Below you will see how to use Technique 3 to correlate elements, simpleType, complexType, groups, and attributeGroups with this merged entity.

Defining XSD elements

An XML schema can be defined in multiple ways to describe the same XML document or fragment. For example, to describe the XML fragment in Listing 9, an XML schema can be defined in multiple ways as shown in Listing 10. Therefore an XSL designed to process XSD documents should account for such variations in schema definitions.

Listing 9. Sample XML Fragment
<UserAuthRequest>
    <UserLoginName id="#123">Ramkumar</UserLoginName>
</UserAuthRequest>
Listing 10. XML Schema Snippet
Method 1:
<xs:element name="UserAuthRequest">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="UserLoginName"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
<xs:element name="UserLoginName" type="UserLoginNameType"/>
<xs:complexType name="UserLoginNameType">
    <xs:simpleContent>
        <xs:extension base="xs:NCName">
            <xs:attribute name="id" use="required" type="xs:integer"/>
        </xs:extension>
    </xs:simpleContent>
</xs:complexType>

Method 2:
<xs:element name="UserAuthRequest">
    <xs:complexType>
        <xs:sequence>
            <xs:element name="UserLoginName" type="UserLoginNameType"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
<xs:complexType name="UserLoginNameType">
    <xs:simpleContent>
        <xs:extension base="xs:NCName">
            <xs:attribute name="id" use="required" type="xs:integer"/>
        </xs:extension>
    </xs:simpleContent>
</xs:complexType>

Method 3:
<xs:element name="UserAuthRequest">
    <xs:complexType>
        <xs:sequence>
            <xs:element ref="UserLoginName"/>
        </xs:sequence>
    </xs:complexType>
</xs:element>
<xs:element name="UserLoginName">
    <xs:complexType>
        <xs:simpleContent>
            <xs:extension base="xs:NCName">
                <xs:attribute name="id" use="required" type="xs:integer"/>
            </xs:extension>
        </xs:simpleContent>
    </xs:complexType>
</xs:element>

Ultimately, the purpose of processing the XSD documents is to understand and derive the XML structure described by a schema. When multiple XSD documents are designed in accordance with heterogeneous namespace design, you can use Technique 2 to merge these multiple schema documents into a merged entity, and use the templates in Technique 3 in Listing 11 below to correlate elements, simpleType, complexType, groups, and attributeGroups defined within this merged entity.

Technique 3

Technique 3 uses a pull model of parsing through the XSD document to derive the XML structure described by a schema. Generally in a schema, any element defined at the top level can be treated as a root element of the XML structure. Therefore, when deriving an XML structure from the schema, it is important to mention the relevant root element for which want to derive the XML structure. For example, Technique 3 XSL templates as shown in Listing 11 use Company as its interested root element. Listing 11 shows some important templates that help correlate xs:element with other types. To get the complete XSL, you can download the source code related to Technique 3 at the bottom of the article.

Listing 11. Technique 3 XSL
<!-- User need to enter the interested element name here manually -->
<!-- For an example, this template uses 'Company' as the interested element name -->
<xsl:template match="xs:element[@name='Company']">
    <xsl:apply-templates select="." mode="internal"/>
</xsl:template>

<!-- Template that deals with all the variations of xsd:element definitions -->
<xsl:template match="xs:element" mode="internal">
<xsl:choose>
        <xsl:when test="@ref">
            <xsl:variable name="refnamespace">
                <xsl:call-template name="namespace-uri-of-ref">
                    <xsl:with-param name="element" select="."/>		
                </xsl:call-template>
            </xsl:variable>
            <xsl:variable name="reflocalname">
                <xsl:call-template name="local-name-of-qname">
                    <xsl:with-param name="qname" select="@ref"/>
                </xsl:call-template>
            </xsl:variable>      
            <xsl:apply-templates select="//xs:element[@name=$reflocalname and 
                @targetNamespace=$refnamespace]" mode="internal"/>
    </xsl:when>
    <xsl:otherwise>
        <xsl:text disable-output-escaping="yes"><![CDATA[<]]></xsl:text>
        <xsl:value-of select="@name"/>      
        <xsl:apply-templates select="./xs:complexType" mode="attributes"/>
        <xsl:text disable-output-escaping="yes"><![CDATA[>]]></xsl:text>

        <!-- Check if the type is an internal type or a complex type -->
        <xsl:if test="@type">
            <xsl:variable name="typenamespace">
                <xsl:call-template name="namespace-uri-of-type">
                    <xsl:with-param name="element" select="."/>
                </xsl:call-template>
            </xsl:variable>
            <xsl:choose>
            <xsl:when test="contains($typenamespace,'http://www.w3.org/2001/XMLSchema')">
                <xsl:call-template name="printelement">
                    <xsl:with-param name="element" select="."/>
                    <xsl:with-param name="showtype" select="'yes'"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="printelement">
                    <xsl:with-param name="element" select="."/>
                    <xsl:with-param name="showtype" select="'no'"/>
                </xsl:call-template>
                <xsl:if test="@substitutionGroup">
                    <xsl:variable name="subtitutelocalname">
                        <xsl:call-template name="local-name-of-qname">
                            <xsl:with-param name="qname" select="@substitutionGroup"/>
                        </xsl:call-template>
                        </xsl:variable>
                        <xsl:text>substitutionGroup=</xsl:text>
                        <xsl:value-of select="$subtitutelocalname"/>
                        <xsl:text>;</xsl:text>
                    </xsl:if>
                    <xsl:variable name="typelocalname">
                        <xsl:call-template name="local-name-of-qname">
                            <xsl:with-param name="qname" select="@type"/>
                        </xsl:call-template>
                    </xsl:variable>
                    <xsl:apply-templates select="//xs:complexType[@name=$typelocalname and
                        @targetNamespace=$typenamespace]" mode="internal"/>
                </xsl:otherwise>
                </xsl:choose>
    </xsl:if>

    <xsl:apply-templates select="./xs:complexType" mode="elements"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[</]]></xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text disable-output-escaping="yes"><![CDATA[>]]></xsl:text>

    <!-- Check for substitutionGroup for this elements -->
    <xsl:variable name="localname" select="@name"/>
    <xsl:variable name="targetNamespace" select="@targetNamespace"/>
    <xsl:for-each select="//xs:element[@substitutionGroup=$localname and 
        @targetNamespace=$targetNamespace]">
            <xsl:apply-templates select="." mode="internal"/>
    </xsl:for-each>
    <xsl:variable name="prefixedlocalname">
        <xsl:value-of select="@targetNamespacePrefix"/>
        <xsl:text>:</xsl:text>
        <xsl:value-of select="@name"/>
    </xsl:variable>
    <xsl:for-each select="//xs:element[@substitutionGroup=$prefixedlocalname]">
            <xsl:apply-templates select="." mode="internal"/>
        </xsl:for-each>
    </xsl:otherwise>
</xsl:choose>
</xsl:template>

Listing 12 shows a sample output generated using Technique 3 XSL when applied over the generated merged schema entity as shown in Listing 8 above. The XML shown in Listing 12 shows the structure of XML as described by the schema for an element called Company, and also demonstrates how the schema properties like minOccurs, maxOccurs, type, substitutionGroup, and others can be maintained within this structure:

Listing 12. Technique 3 Sample Output
<?xml version="1.0" encoding="UTF-8"?>
<Company>
    <Person>maxOccurs=unbounded;minOccurs=1;
        <Name>type=xs:string;maxOccurs=1;minOccurs=1;</Name>
        <SSN>type=xs:string;maxOccurs=1;minOccurs=1;</SSN>
        <Address>maxOccurs=unbounded;minOccurs=0;
            <Street>type=xs:string;maxOccurs=1;minOccurs=1;</Street>
            <City>type=xs:string;maxOccurs=1;minOccurs=1;</City>
            <Country>type=xs:string;maxOccurs=1;minOccurs=1;</Country>
        </Address>
    </Person>
    <Product>maxOccurs=unbounded;minOccurs=1;
        <WebSphere>maxOccurs=1;minOccurs=1;
            <version>type=xs:string;maxOccurs=1;minOccurs=1;</version>
            <features>type=xs:string;maxOccurs=1;minOccurs=1;</features>
        </WebSphere>
        <DB2>maxOccurs=1;minOccurs=1;substitutionGroup=WebSphere;
            <version>type=xs:string;maxOccurs=1;minOccurs=1;</version>
            <features>type=xs:string;maxOccurs=1;minOccurs=1;</features>
        </DB2>
        <Lotus>maxOccurs=1;minOccurs=1;substitutionGroup=WebSphere;
            <version>type=xs:string;maxOccurs=1;minOccurs=1;</version>
            <features>type=xs:string;maxOccurs=1;minOccurs=1;</features>
        </Lotus>
    </Product>
</Company>

You can also merge the XSL templates in Technique 2 and Technique 3 within a single XSL stylesheet to directly process the input schema documents, as shown in Listing 6 above, and generate the output as shown in Listing 12 above. This approach is also demonstrated in technique3.xsl, which you can download at the bottom of the article.

Conclusion

This article described:

  • Challenges of processing multiple XSD documents with XSLT V1.0
  • The three different schema design approaches, and some techniques for implementing the three approaches.
  • Considerations when designing your XSL to deal with XSD documents, such as variations in the schema definition used to define a similar XML structure, and how to efficiently merge multiple schema documents.
  • Dealing with multiple targetNamespaces, and how to correlate elements, simpleTypes, complexTypes, groups, and attributeGroups distributed across multiple schema files
  • Dealing with complex schema-based rules such as substitutionGroups
  • Representing schema rules in the form of simple XML and maintain other schema-related information within this XML structure, as shown in Listing 12 above

Download

DescriptionNameSize
Code samplesample-code.zip13 KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=603469
ArticleTitle=Processing XML schemas with XSLT for WebSphere DataPower SOA Appliances
publish-date=12222010