2

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>
4
  • How do you know where the comment belongs? For example, how should your XSLT code decide whether the second set of comments should be transformed to an xs:annotation of neque or tincidunt? Commented Aug 19, 2014 at 10:24
  • There is a logical structure in this XSD. Subsequent comments belong always to the immediately preceding simpleType or complexType. For example the comment starting with <!-- Lorem ipsum dolo... belongs to simpleType ultricies. Commented Aug 19, 2014 at 11:54
  • In this case, it is an interesting programming problem that can be solved. See my answer for details. Commented Aug 19, 2014 at 12:31
  • Both proposals work. Many thanks for the quick response. Commented Aug 19, 2014 at 15:06

2 Answers 2

3

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:simpleTypeor 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:simpleTypeor 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:

  • If element nodes are matched by a template, it is necessary to first add all attributes to the result tree. After a child element has been added to the result tree, no more attribute nodes can be added.
  • There is another template to match comment nodes - and do nothing, since they are all processed inside another template.

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.

Sign up to request clarification or add additional context in comments.

Comments

2

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.