Reputation: 25
I have a XSD that has a number of comments, which should be moved into the a xs:annotation/xs:documentation part of the preceding xs:simpleTypes or xs:complexTypes. How can I move these comments using XSLT V1.0 or XSLT V2.0?
Example input XSD:
<?xml version="1.0" encoding="UTF-8"?>
<xs:simpleType name="ligula">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="ultricies">
<xs:restriction base="xs:integer"/>
</xs:simpleType>
<!--
Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Aenean commodo ligula eget dolor. Aenean massa. Cum
-->
<xs:simpleType name="neque">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="tincidunt">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<!-- ligula, porttitor eu, consequat vitae, eleifend ac, enim. -->
<!-- Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed. -->
<!-- Integer tincidunt. Cras dapibus. Vivamus elementum -->
</xs:schema>
The output XSD should be:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="ligula">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="ultricies">
<xs:annotation>
<xs:documentation>Lorem ipsum dolor sit amet, consectetuer adipiscing elit.
Aenean commodo ligula eget dolor. Aenean massa. Cum</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:integer"/>
</xs:simpleType>
<xs:simpleType name="neque">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="tincidunt">
<xs:annotation>
<xs:documentation>ligula, porttitor eu, consequat vitae, eleifend ac, enim</xs:documentation>
<xs:documentation>Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed.</xs:documentation>
<xs:documentation>Integer tincidunt. Cras dapibus. Vivamus elementum</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string"/>
</xs:simpleType>
</xs:schema>
Upvotes: 2
Views: 172
Reputation: 22617
The following stylesheet transforms each comment node into an xs:documentation
element. More precisely, I store in a variable the sequence of comment nodes that immediately follow an xs:simpleType
or xs:complexType
element:
<xsl:variable name="com" select="following-sibling::comment() except following-sibling::*[local-name() = 'simpleType' or local-name() = 'complexType'][1]/following-sibling::comment()"/>
The expression looks for all following siblings that are comment nodes and subtracts all comment nodes that are a following sibling of the immediately following xs:simpleType
or xs:complexType
element (I know, it sounds complicated).
If this sequence is not empty:
<xsl:if test="$com">
Then, an xs:annotation
element is inserted and the string value of each comment node is turned into an xs:documentation
element:
<xs:annotation>
<xsl:for-each select="$com">
<xs:documentation>
<xsl:value-of select="normalize-space(.)"/>
</xs:documentation>
</xsl:for-each>
</xs:annotation>
Other details:
Stylesheet
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xs:simpleType|xs:complexType">
<xsl:copy>
<xsl:apply-templates select="@*"/>
<xsl:variable name="com" select="following-sibling::comment() except following-sibling::*[local-name() = 'simpleType' or local-name() = 'complexType'][1]/following-sibling::comment()"/>
<xsl:if test="$com">
<xs:annotation>
<xsl:for-each select="$com">
<xs:documentation>
<xsl:value-of select="normalize-space(.)"/>
</xs:documentation>
</xsl:for-each>
</xs:annotation>
</xsl:if>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="comment()"/>
</xsl:transform>
XML Output
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:simpleType name="ligula">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="ultricies">
<xs:annotation>
<xs:documentation>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:integer"/>
</xs:simpleType>
<xs:simpleType name="neque">
<xs:restriction base="xs:string"/>
</xs:simpleType>
<xs:simpleType name="tincidunt">
<xs:annotation>
<xs:documentation>ligula, porttitor eu, consequat vitae, eleifend ac, enim.</xs:documentation>
<xs:documentation>Etiam sit amet orci eget eros faucibus tincidunt. Duis leo. Sed.</xs:documentation>
<xs:documentation>Integer tincidunt. Cras dapibus. Vivamus elementum</xs:documentation>
</xs:annotation>
<xs:restriction base="xs:string"/>
</xs:simpleType>
</xs:schema>
Try this solution online here if you'd like to make changes.
Upvotes: 3
Reputation: 163322
Try
<xsl:template match="xs:schema">
<xsl:copy>
<xsl:for-each-group group-starting-with="xs:simpleType|xs:complexType">
<xsl:apply-templates select="." mode="include-comments">
<xsl:with-param name="comments" select="current-group()/self::comment()"/>
</
</
</
</
<xsl:template match="*" mode="include-comments">
<xsl:param name="comments" as="comment()*"/>
<xsl:copy>
<xs:documentation>
<xsl:for-each select="$comments">
<xs:annotation><xsl:value-of select="."/>
</xsl:for-each>
</
</
</
Note: on a typical implementation this is likely to have linear performance, whereas Matthias' solution is likely to have quadratic performance.
Upvotes: 2