[xsl] Generate instance document from schema

Subject: [xsl] Generate instance document from schema
From: "v.omprakash" <omprakashv@xxxxxxxxxxxxxxxxxx>
Date: Sun, 16 Jul 2006 09:49:55 +0530
 Hi,
      Over at the schema list someone had asked if there was a xslt
stylesheet that could generate an instance from a schema. I have been trying
without much luck to get my posting across without much luck. So Iam posting
my stylesheet here for the benefit of the OP as well as the XSLT list
members.

   Here's my stylesheet that uses XSLT 2.0. Iam guessing that it should be
possible to make it work with XSLT 1.0 by changing the version attribute
though I haven't tried it myself. It is not complete as yet and still
requires some work. Hope you find it useful.



<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform";
xmlns:xsd="http://www.w3.org/2001/XMLSchema";>
<xsl:strip-space elements="*"/>

<!--
	purpose is to generate a transform that will lable each element in an
instance document with
	its own Normalised Universal Name (NUN), and with that of its schema type.

	The elementNUN will be:
		"<ns>#/element::<elementName>" if it is a global element, or
		"<typeNUN>/element::<elementName>" if it is a local element

	The typeNUN will be:
		"<ns>#/type::<typeName>" if it's a named type, or
		"<elementNUN>/type::*" if it's anonymous.

	(where "<x>" means the runtime value of "x", and "[y]+" indicates one or
more "y")

	The use of Normalized Universal Names is based on
	http://www.w3.org/TR/2001/WD-xmlschema-formal-20010320/#section-overview-no
rmalization

-->
    	<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

	<xsl:key name="gnt" match="node()" use="@name"/>

	<!-- get the root element into a var so we can get back after recursively
processing included XSDs -->
	<xsl:variable name="root" select="/"/>

	<xsl:param name="rootelem" select="'purchaseOrder'"/>

	<!-- get XML Schema namespace -->
	<xsl:variable name="xsd-uri" select="namespace-uri(/*)" />


	<!-- now start at the top -->
	<xsl:template match="/">

		<!-- get the top level elements -->
		<xsl:comment>typeTagger generated by
typeTagger.compiler.xslt</xsl:comment>
		<!-- call the schema definition template ... -->
		<xsl:call-template name="gatherSchema">
			<!-- ... with current current root as the $schemas parameter ... -->
			<xsl:with-param name="schemas" select="/"/>
			<!-- ... and any includes in the $include parameter -->
			<xsl:with-param name="includes"
select="document(/xsd:schema/xsd:include/@schemaLocation)"/>
		</xsl:call-template>
	</xsl:template>
	<!-- -->
	<!-- gather all included schemas into a single parameter variable -->
	<!-- -->
	<xsl:template name="gatherSchema">
		<xsl:param name="schemas"/>
		<xsl:param name="includes"/>
		<xsl:message>in gatherSchema...</xsl:message>

		<xsl:choose>
			<xsl:when test="count($schemas) &lt; count($schemas | $includes)">
				<xsl:message>recursing back into gatherSchema...</xsl:message>
				<!-- when $includes includes something new, recurse ... -->
				<xsl:call-template name="gatherSchema">
					<!-- ... with current $includes added to the $schemas parameter ... -->
					<xsl:with-param name="schemas" select="$schemas | $includes"/>
					<!-- ... and any *new* includes in the $include parameter -->
					<xsl:with-param name="includes"
select="document($includes/xsd:schema/xsd:include/@schemaLocation)"/>
				</xsl:call-template>
			</xsl:when>

			<xsl:otherwise>
				<xsl:message>count($schemas//xsd:element): <xsl:value-of
select="count($schemas//xsd:element)"/>
				</xsl:message>
				<!-- process the schemas -->
				<!-- start by creating our target transform root element -->
					<!-- kick off the global elements and type definitions -->

	    <!--
					<xsl:apply-templates select="$schemas//xsd:schema/xsd:element |
$schemas//xsd:schema/xsd:complexType | $schemas//xsd:schema/xsd:simpleType"
mode="typeTag">
					    -->


					<xsl:apply-templates select="$schemas//xsd:schema/xsd:element[@name=
$rootelem]" mode="typeTag">


						<xsl:with-param name="schemas" select="$schemas"/>
					</xsl:apply-templates>

			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	<!--
		plan is:

		for each element:
			write a template that matches by element name and mode=parent-typeNUN (if
any)
				with apply-templates to content type with mode=this-typeNUN
			apply-templates to anonymous type definitions with this-elementNun as a
parameter

		for each type definition:
			write mop-up template with mode=this-typeNUN for non-element nodes
			apply-templates to content elements with this-typeNUN as a parameter

	-->
	<!-- -->
	<!-- create templates to tag elements -->
	<!-- -->


	<xsl:template match="xsd:element" mode="typeTag">
		<xsl:param name="schemas"/>
		<xsl:param name="context"/>

		<xsl:choose>
		<xsl:when test="@type[substring-before(., ':') !=
name(current()/namespace::*[. = $xsd-uri])]">
		<!-- this element has a global, named type -->

		<xsl:variable name="gntvar" select="key('gnt', @type)"/>

		<xsl:if test="self::node()[@minOccurs][@minOccurs &gt;  0]  or
self::node()[not(@minOccurs)]">

		<xsl:element name="{@name}">
		    <xsl:for-each select="$gntvar//xsd:attribute">
		        <xsl:attribute name="{@name}">
		            <xsl:value-of select="''"/>
		            </xsl:attribute>
		    </xsl:for-each>
    		    <xsl:text> </xsl:text>

		    <xsl:apply-templates select="$gntvar/*" mode="typeTag">
		    <xsl:with-param name="schemas" select="$schemas"/>
		    </xsl:apply-templates>

		</xsl:element>

		 </xsl:if>

		</xsl:when>

		<xsl:when test="@ref">

		<!-- this element refers to a global element which may have named or
anonymous type -->

		<xsl:variable name="refElement"
select="$schemas//xsd:schema/xsd:element[@name=current()/@ref]"/>

		<xsl:choose>
		<xsl:when test="$refElement/@type[substring-before(., ':') !=
name($refElement/namespace::*[. = $xsd-uri])]">
	    <!--  the referred-to element has a named type -->

	    	<xsl:apply-templates select="key('gnt',
$refElement/@type)//xsd:element" mode="typeTag">
		    <xsl:with-param name="schemas" select="$schemas"/>
		    </xsl:apply-templates>

	   	 </xsl:when>
		<xsl:otherwise>

   		<xsl:variable name="refElement"
select="$schemas//xsd:schema/xsd:element[@name=current()/@ref]"/>

		<!--  the referred-to element has anonymous type  -->
		<xsl:if test="$refElement[(@minOccurs and @minOccurs &gt;  0)  or
(not(@minOccurs))]">

		<xsl:element name="{$refElement/@name}">
		    <xsl:text> </xsl:text>

		    <xsl:for-each select="$refElement//xsd:attribute">
		        <xsl:attribute name="{@name}"/>
		    </xsl:for-each>
		    <xsl:apply-templates select="$refElement/*">
		    <xsl:with-param name="schemas" select="$schemas"/>
		    </xsl:apply-templates>
		    </xsl:element>

		 </xsl:if>


		</xsl:otherwise>
		</xsl:choose>

		</xsl:when>
		<xsl:otherwise>

		<!-- this element has an anonymous type -->
			<xsl:element name="{@name}">
			    <xsl:text> </xsl:text>

			    <xsl:for-each select="xsd:attribute">
			        <xsl:value-of select="@name"/>

			        </xsl:for-each>
				<xsl:apply-templates>
			                             <xsl:with-param name="schemas"
select="$schemas"/>
			                             </xsl:apply-templates>
			</xsl:element>
				</xsl:otherwise>
		</xsl:choose>

		<!-- apply templates with mode="typeTag" to the element content if any -->
		<xsl:apply-templates select=".//xsd:complexType[1] | .//xsd:simpleType[1]"
mode="typeTag">
			<xsl:with-param name="schemas" select="$schemas"/>
		</xsl:apply-templates>

			<!-- apply templates with mode="typeTag" to the element content if
any -->

	</xsl:template>

	<xsl:template match="xsd:sequence|xsd:any|xsd:all" mode="typeTag">
	    	<xsl:param name="schemas"/>



	    	<xsl:apply-templates select="*" mode="typeTag">
			<xsl:with-param name="schemas" select="$schemas"/>
		</xsl:apply-templates>
	</xsl:template>

	<xsl:template match="text()" mode="typeTag">
	    <xsl:value-of select="."/>
	    </xsl:template>



</xsl:stylesheet>

cheers,
prakash

Current Thread