xmlxsltxslt-1.0wordmlwordprocessingml

Transform Docbook XML lists into WordML using XSLT stylesheet


I have the following XML I'd like to translate to WordML:

<document>
<section>
    <para>An ordered list:</para>
    <orderedlist>
        <listitem>first list item</listitem>
        <listitem>second list item
            <orderedlist>
                <listitem>one</listitem>
                <listitem>two
                    <orderedlist>
                        <listitem>one</listitem>
                        <listitem>two</listitem>
                        <listitem>three</listitem>
                    </orderedlist>
                </listitem>
                <listitem>three</listitem>
            </orderedlist>
        </listitem>
        <listitem>third list item</listitem>
    </orderedlist>
</section>
</document>

This is my XSLT 1.0 stylesheet template so far:

<xsl:template match="document/section/orderedlist">
  <xsl:for-each select="listitem">
  <w:p>
    <w:pPr><w:pStyle w:val="Normal"/><w:numPr>
            <w:ilvl w:val="{count(ancestor::orderedlist)-1}"/>  <!-- level of list item (0,1,2 etc.) -->
            <w:numId w:val="{position()}"/>  <!-- the orderedlist should get an ID (1,2,3 etc.) -->
        </w:numPr>
    </w:pPr>
    <w:r><w:t><xsl:value-of select="."/></w:t></w:r>
  </w:p>
  <xsl:apply-templates />
  </xsl:for-each>
</xsl:template>

But this

  1. does not work for the nested lists.
  2. the ID of the list ("position()") should be the same for every listitem of an orderedlist.

Can anybody help me out? Especially with the nested list part.

See: http://xsltransform.net/jyH9rN8

Expected output:

<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>first list item</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>second list item</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="1"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>one</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="1"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>two</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="2"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>one</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="2"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>two</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="2"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>three</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="1"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>three</w:t></w:r>
</w:p>
<w:p>
  <w:pPr><w:pStyle w:val="Normal"/><w:numPr><w:ilvl w:val="0"/><w:numId w:val="1"/></w:numPr></w:pPr>
  <w:r><w:t>third list item</w:t></w:r>
</w:p>

Solution

  • It's difficult to read your code because (a) it's not indented properly and (b) it's taken out of context. You could probably have removed a significant portion of it too, leaving only what's essential to understanding the problem.

    I suspect something like this could work you:

    XSLT 1.0

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="/document">
        <w:wordDocument>
            <xsl:apply-templates/>
        </w:wordDocument>
    </xsl:template>
    
    <xsl:template match="listitem">
        <w:p>
            <w:pPr>
                <w:pStyle w:val="Normal"/>
                <w:numPr>
                    <!-- level of list item (0,1,2 etc.) -->
                    <w:ilvl w:val="{count(ancestor::orderedlist)-1}"/>
                    <!-- the ordered list should get an ID (1,2,3 etc.) -->
                    <w:numId w:val="{generate-id(..)}"/>
                </w:numPr>
            </w:pPr>
            <w:r>
                <w:t>
                    <xsl:value-of select="text()"/>
                </w:t>
            </w:r>
        </w:p>
        <xsl:apply-templates />
    </xsl:template>
    
    <xsl:template match="text()"/>
    
    </xsl:stylesheet>
    

    When applied to your input example, the result will be:

    <?xml version="1.0" encoding="UTF-8"?>
    <w:wordDocument xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml">
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="0"/>
                <w:numId w:val="d0e5"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>first list item</w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="0"/>
                <w:numId w:val="d0e5"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>second list item
                    </w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="1"/>
                <w:numId w:val="d0e10"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>one</w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="1"/>
                <w:numId w:val="d0e10"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>two
                            </w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="2"/>
                <w:numId w:val="d0e15"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>one</w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="2"/>
                <w:numId w:val="d0e15"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>two</w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="2"/>
                <w:numId w:val="d0e15"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>three</w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="1"/>
                <w:numId w:val="d0e10"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>three</w:t>
          </w:r>
       </w:p>
       <w:p>
          <w:pPr>
             <w:pStyle w:val="Normal"/>
             <w:numPr>
                <w:ilvl w:val="0"/>
                <w:numId w:val="d0e5"/>
             </w:numPr>
          </w:pPr>
          <w:r>
             <w:t>third list item</w:t>
          </w:r>
       </w:p>
    </w:wordDocument>
    

    or similar (the id format is processor-dependent).