XML.com: XML From the Inside Out
oreilly.comSafari Bookshelf.Conferences.

advertisement

Transforming XML Schemas
by Eric Gropp | Pages: 1, 2

When an <extension> is encountered, the stylesheet will process the extension's base and local content models in sequence.

<xsl:template match="xs:extension" mode="findChildNodes">
  <xsl:apply-templates select="/xs:schema/xs:complexType[@name=current()/@base]" 
                       mode="findChildNodes"/>
  <xsl:apply-templates select="*" mode="findChildNodes"/>
</xsl:template>

Once the definition of the child element is found, the template determines whether it is a simple type and exempt from any application specific restrictions. If this is true, it builds the table row, and then applies the templates from the childNodeInput mode to construct the input element.

<xsl:template match="xs:element[@name]" mode="findChildNodes">
  <xsl:if test="not(xs:complexType|
                /xs:schema/xs:complexType[@name=current()/@type]|
                xs:annotation/xs:appinfo/frm:readonly)">

    <tr>
      <td>
        <xsl:choose>
          <xsl:when test="xs:annotation/xs:appinfo/frm:label">	
            <xsl:value-of select="xs:annotation/xs:appinfo/frm:label"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="@name"/>
          </xsl:otherwise>	
        </xsl:choose>

      </td>
      <td>
        <xsl:apply-templates select="." mode="childNodeInput">
          <xsl:with-param name="nodeName" select="@name"/>
        </xsl:apply-templates>

      </td>
    </tr>

  </xsl:if>
</xsl:template>

<xsl:template match="*" mode="findChildNodes"/>

Interpreting Simple Type Definitions

The templates of the simpleInputElement mode will walk the schema to find the base type of each simple element and then output the appropriate XHTML form element for the type. If the element's type is a restriction of a base type, it will further modify the XHTML form element with XHTML or custom attributes.

Model of the childNodeInput mode

The first two template rules are designed to match elements typed with anonymous or namespace defined simple types. The priority attribute of the first template is set to zero, so that it will have a lower priority than the template rules matching native WXS types.

<xsl:template match="xs:element[@type]" mode="childNodeInput" priority="0">
  <xsl:param name="nodeName"/>
  <xsl:param name="nodeValue" select="@default"/>
  <xsl:apply-templates 
       select="/xs:schema/xs:simpleType[@name=current()/@type]/xs:restriction"
       mode="childNodeInput">
    <xsl:with-param name="nodeName" select="$nodeName"/>
    <xsl:with-param name="nodeValue" select="$nodeValue"/>
  </xsl:apply-templates>
</xsl:template>

<xsl:template match="xs:element[xs:simpleType]" mode="childNodeInput">
  <xsl:param name="nodeName"/>
  <xsl:param name="nodeValue" select="@default"/>
   <xsl:apply-templates select="xs:simpleType/xs:restriction" mode="childNodeInput">
    <xsl:with-param name="nodeName" select="$nodeName"/>
    <xsl:with-param name="nodeValue" select="$nodeValue"/>
  </xsl:apply-templates>
</xsl:template>

The following template rules are a sample of the templates to match the native WXS types.

<xsl:template match="xs:element[@type='xs:string']|xs:restriction[@base='xs:string']"
              mode="childNodeInput">
  <xsl:param name="nodeName"/>
  <xsl:param name="nodeValue" select="@default"/>
    <xsl:choose>
      <xsl:when test="xs:maxLength">
        <input type="text" name="{$nodeName}" value="{$nodeValue}">
          <xsl:apply-templates select="*" mode="childNodeInput"/>
        </input>					
      </xsl:when>
      <xsl:otherwise>
        <textArea name="{$nodeName}">
          <xsl:apply-templates select="*" mode="childNodeInput"/>
          <xsl:value-of select="$nodeValue"/>
        </textArea>
      </xsl:otherwise>
    </xsl:choose>
</xsl:template>	

<xsl:template match="xs:element[@type='xs:boolean']|xs:restriction[@base='xs:boolean']"
              mode="childNodeInput">
  <xsl:param name="nodeName"/>
  <xsl:param name="nodeValue" select="@default"/>
  <input type="radio" name="{$nodeName}" value="true">
    <xsl:if test="$nodeValue='true'">
      <xsl:attribute name="checked">checked</xsl:attribute>
    </xsl:if>
    Yes
  </input>
  <input type="radio" name="{$nodeName}" value="false">
    <xsl:if test="$nodeValue='false'">
      <xsl:attribute name="checked">checked</xsl:attribute>
    </xsl:if>
    No
  </input>
</xsl:template>

Some restriction facets map directly to XHTML input element attributes. For example <xs:maxLength value="10"> maps to maxsize="10" attribute in an <input> element. Other restriction facets such as <xs:pattern> and <xs:maxInclusive> do not map to XHTML nodes. One approach to ensure conformant input from the user is to add custom attributes to the XHTML <input> element, and use a client side script to validate the user's input using the custom attributes.

<xsl:template match="xs:maxLength" mode="childNodeInput">
  <xsl:attribute name="maxLength">
    <xsl:value-of select="@value"/>
  </xsl:attribute>
</xsl:template>

<xsl:template match="xs:pattern" mode="childNodeInput">
  <xsl:attribute name="validationRegExp">
    <xsl:value-of select="@value"/>
  </xsl:attribute>
</xsl:template>

This stylesheet will map restrictions that have enumeration facets, regardless of the base type, to a <select> XHTML element. WXS annotation elements can useful here, when we need to associate human-readable text with an enumeration facet:

<xs:enumeration value="AK">
  <xs:annotation>
    <xs:documentation>Alaska</xs:documentation>
  </xs:annotation>
</xs:enumeration>
Will be transformed into:

<option value="AK">Alaska</option>

The template rule that matches each enumeration facet checks for annotation information as well as whether the enumeration is the default value.

<xsl:template match="xs:restriction[xs:enumeration]" mode="childNodeInput" 
              priority="1">
  <xsl:param name="nodeName"/>
  <xsl:param name="nodeValue"/>
  <select name="{$nodeName}">
    <xsl:apply-templates select="*" mode="childNodeInput">
      <xsl:with-param name="nodeValue" select="$nodeValue"/>
    </xsl:apply-templates>
  </select>
</xsl:template>

<xsl:template match="xs:enumeration" mode="childNodeInput">
  <xsl:param name="nodeValue"/>
  <option value="{@value}">
    <xsl:if test="@value=$nodeValue">
      <xsl:attribute name="selected">
        selected
      </xsl:attribute>
    </xsl:if>
    <xsl:choose>
      <xsl:when test="xs:annotation/xs:documentation">
        <xsl:value-of select="xs:annotation/xs:documentation"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="@value"/>
      </xsl:otherwise>
    </xsl:choose>
  </option>
</xsl:template>

<xsl:template match="*" mode="childNodeInput"/>

Conclusion

Using WXS as the common resource for data typing in your application can have big payoffs. By allowing components and interfaces to automatically reflect changes to an application's data model, you can greatly increase the reusability and flexibility of a system. XSLT is a useful, largely platform-independent, and highly portable tool for making this possible.



1 to 5 of 5
  1. same thing for relaxNG ?
    2006-11-17 03:20:34 ndeb
  2. Which extensions needed for imports (includes)
    2004-07-21 03:45:44 mattad
  3. adding MVC and i18n
    2003-05-12 11:43:15 Erik Ostermueller
  4. Does it work?
    2003-01-29 09:36:42 Rogier Peters
  5. how to handle minOccurs and maxOccurs
    2003-01-21 02:03:07 Edwin van der Wal
1 to 5 of 5