For a project relating to Amazon, I found a need for an XSL template that could convert their XSD files–which are both numerous and meaty–into complete XML prototypes. This would be useful for two reasons.
First, it would generate a complete sample call for basically any API request for which the provide an XSD. Second, it would give me XML that I could programmatically convert to a self-generating form if necessary.
After digging around the web, to my surprise I was unable to find an XSL already made that accomplishes this, so I set about making my own.
The resulting XSL is included below.
Please note that while this appears (as of this writing) to be 100% functional for what I need on Amazon, it does not cover all possible XSD configurations, so may need to be modified for more exotic XSDs. Also, it’s possible (and quite likely) that the XML generated will not validate to the given XSD. This is because I had to find ways to pass value restriction meta data in a way that made sense in XML.
The two obvious points are nodes with enumerations in which the valid values are passed as <Value> subnodes and nodes with extendable attributes that had to be constructed in the way of other nodes. In both cases, a flag is set as an attribute in the parent node.
If you make enhancements or fixes or if you find problems, feel free to let me know, and I will revise this.
The XSL is free for anyone to use, modify, etc. without restraint, but I’d certainly be happy to hear about it if it helps you out.
Here is the code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
version="1.0"
>
<xsl:output method=”xml”/>
<!– Ignore nodes that may have text values as they will output directly otherwise –>
<xsl:template match=”*[not(xs:element) and not(xs:include)]” />
<!– Uncomment to process any included files inline–>
<!–
<xsl:template match=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)/xs:schema/xs:element”>
<xsl:call-template name=”element” />
</xsl:for-each>
</xsl:template>
–>
<!– ######################
## XML ELEMENTS TEMPLATE ##
####################### –>
<xsl:template match=”/xs:schema/xs:element” name=”element”>
<xsl:choose>
<!– Node is referencing a base element – use that for the prototype either from this file or from any included files–>
<xsl:when test=”@ref”>
<xsl:variable name=”elementRef” select=”current()/@ref” />
<xsl:for-each select=”//xs:element[@name=$elementRef]“><xsl:call-template name=”element”/></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:element[@name=$elementRef]“><xsl:call-template name=”element”/></xsl:for-each>
</xsl:for-each>
</xsl:when>
<!– The node is a standard element – output its XML Prototype –>
<xsl:otherwise>
<xsl:element name=”{@name}”>
<!– Carry all attributes through other than name (which is used for the element nodeName) and type (which we have to handle cases for) –>
<xsl:copy-of select=”@*[local-name() != 'name' and local-name() != 'type']” />
<!– Bring in any annotations for the element as a “note” attribute –>
<xsl:if test=”./xs:annotation/xs:documentation”>
<xsl:attribute name=”note”><xsl:value-of select=”./xs:annotation/xs:documentation” /></xsl:attribute>
</xsl:if>
<xsl:choose>
<!– If there is a type attribute, it implies that it’s a complexType node and so we’ll be handling its subNodes independently –>
<xsl:when test=”contains(@type,’:')”>
<xsl:variable name=”elementType” select=”substring-after(@type,’:')” />
<xsl:for-each select=”//xs:complexType[@name=$elementType]“><xsl:call-template name=”complexType”/></xsl:for-each>
<xsl:for-each select=”//xs:simpleType[@name=$elementType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:complexType[@name=$elementType]“><xsl:call-template name=”complexType”/></xsl:for-each>
<xsl:for-each select=”document(@schemaLocation)//xs:simpleType[@name=$elementType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
</xsl:for-each>
</xsl:when>
<xsl:when test=”@type”>
<xsl:variable name=”elementType” select=”@type” />
<xsl:for-each select=”//xs:complexType[@name=$elementType]“><xsl:call-template name=”complexType”/></xsl:for-each>
<xsl:for-each select=”//xs:simpleType[@name=$elementType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:complexType[@name=$elementType]“><xsl:call-template name=”complexType”/></xsl:for-each>
<xsl:for-each select=”document(@schemaLocation)//xs:simpleType[@name=$elementType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
</xsl:for-each>
</xsl:when>
<!– This is a direct element definition but it still may have inline definitions –>
<xsl:otherwise>
<xsl:for-each select=”./xs:complexType”><xsl:call-template name=”complexType”/></xsl:for-each>
<xsl:for-each select=”./xs:simpleType”><xsl:call-template name=”simpleType”/></xsl:for-each>
<xsl:for-each select=”./xs:element”><xsl:call-template name=”element”/></xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!– ######################
## COMPLEX TYPE TEMPLATE ##
####################### –>
<xsl:template name=”complexType”>
<!– Pass along all attributes –>
<xsl:copy-of select=”@*[local-name() != 'name']” />
<!– If this complexType needs additional attributes, pass a way to use those –>
<xsl:if test=”./xs:simpleContent/xs:extension/xs:attribute”>
<!– Custom node to indicate that this is node has extended attributes –>
<xsl:attribute name=’isExtended’>1</xsl:attribute>
<!– Element is complexType because it’s an extends the attribute set of a simpleType –>
<xsl:if test=”./xs:simpleContent/xs:extension/@base”>
<xsl:choose>
<xsl:when test=”contains(./xs:simpleContent/xs:extension/@base,’:')”>
<xsl:variable name=”baseSimpleType” select=”substring-after(./xs:simpleContent/xs:extension/@base,’:')” />
<xsl:for-each select=”//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
</xsl:for-each>
</xsl:when>
<xsl:otherwise>
<xsl:variable name=”baseSimpleType” select=”./xs:simpleContent/xs:extension/@base” />
<xsl:for-each select=”//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
</xsl:for-each>
</xsl:otherwise>
</xsl:choose>
</xsl:if>
<!– Pass along all other attributes (other than @base) –>
<xsl:copy-of select=”./xs:simpleContent/xs:extension/@*[local-name() != 'base']” />
<!– Give a container for the actual value of the node –>
<xsl:element name=”NodeValue” />
<!– Show all attributes as sub nodes –>
<xsl:for-each select=”./xs:simpleContent/xs:extension/xs:attribute”>
<xsl:element name=”{@name}”>
<xsl:variable name=”elementType” select=”current()/@type” />
<xsl:for-each select=”//xs:complexType[@name=$elementType]“><xsl:call-template name=”complexType”/></xsl:for-each>
<xsl:for-each select=”//xs:simpleType[@name=$elementType]“><xsl:call-template name=”simpleType”/></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:complexType[@name=$elementType]“><xsl:call-template name=”complexType”/></xsl:for-each>
<xsl:for-each select=”document(@schemaLocation)//xs:simpleType[@name=$elementType]“><xsl:call-template name=”simpleType” /></xsl:for-each>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:if>
<!– Handle any sub nodes –>
<xsl:for-each select=”./xs:sequence/xs:element | ./xs:choice/xs:element”><xsl:call-template name=”element” /></xsl:for-each>
</xsl:template>
<!– #####################
## SIMPLE TYPE TEMPLATE ##
###################### –>
<xsl:template name=”simpleType”>
<!– If there is a base type, analyze and output its attributes first –>
<xsl:choose>
<xsl:when test=”contains(./xs:restriction/@base,’:')”>
<xsl:variable name=”baseSimpleType” select=”substring-after(./xs:restriction/@base,’:')” />
<xsl:attribute name=’{$baseSimpleType}’>{type}</xsl:attribute>
<xsl:for-each select=”//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType” /></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType” /></xsl:for-each>
</xsl:for-each>
</xsl:when>
<xsl:when test=”./xs:restriction/@base”>
<xsl:variable name=”baseSimpleType” select=”./xs:restriction/@base” />
<xsl:attribute name=’{$baseSimpleType}’>{type}</xsl:attribute>
<xsl:for-each select=”//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType” /></xsl:for-each>
<xsl:for-each select=”//xs:include”>
<xsl:for-each select=”document(@schemaLocation)//xs:simpleType[@name=$baseSimpleType]“><xsl:call-template name=”simpleType” /></xsl:for-each>
</xsl:for-each>
</xsl:when>
</xsl:choose>
<!– Pass all type attributes back –>
<xsl:copy-of select=”./@*[local-name() != 'name' and local-name() != 'id']” />
<!– Pass all non-enumeration nodes as attributes (minLength, etc.) –>
<xsl:for-each select=”./xs:restriction/*[local-name() != 'enumeration']“>
<xsl:attribute name=”{local-name()}”><xsl:value-of select=”@value” /></xsl:attribute>
</xsl:for-each>
<!– If enumerations exist, pass an attribute indicating so and pass the values as sub nodes (must come last because it outputs XML directly as text) –>
<xsl:for-each select=”./xs:restriction/xs:enumeration”>
<xsl:if test=”position() = 1″><xsl:attribute name=”isList”>1</xsl:attribute></xsl:if>
<xsl:element name=”Value”><xsl:value-of select=”current()/@value” /></xsl:element>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>



A simple question on Sandusky and Penn State’s chopping block
I have seen a few posts generally asking the question, but I want to weigh in on it as well. The United States is still equipped with a system called due process, but Penn State has already pulled the trigger on many people (most notably Joe Paterno) before any legal judgment has been handed to Sandusky.
Not that I think it will happen, but what if Sandusky is cleared of all charges? Will that not completely invalidate all the action taken by the University?
Posted in Commentary