This question has been raised over at OpenXmlDeveloper.org, and I thought I’d show a sample on my blog. A few things that you need to do in order to create a correct nested list. First up is creation of the numbering definition, and next apply this numbering to various paragraphs inside the main document body. One important note about numbering definitions, each numbering definition (abstract and not abstract) have a unique ID. You need to make sure these ID values are unique for the type of element, start at the value ‘1’ and increment with 1 each new definition, otherwise chaos ensues and the world might end.
Here’s the source XML:
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Orders>
<Order>
<OrderItem></OrderItem>
<OrderItem></OrderItem>
<OrderItem></OrderItem>
</Order>
<Order>
<OrderItem></OrderItem>
<OrderItem></OrderItem>
<OrderItem></OrderItem>
</Order>
</Orders>
Normally a WordProcessingML document is a zip containing various package parts. The sample XSLT I’ll demonstrate outputs a single XML file containing the entire packaging structure. The main template matches the root node of the source XML structure displayed above. When this node is encountered, a whole bunch of packaging related XML is send to the output stream. Most of it is just required markup to create the packaging structure, the numbering part is interesting though. Here’s the contents which is sent to the output document.
<w:numbering xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/3/main">
<w:abstractNum w:abstractNumId="1">
<w:multiLevelType w:val="multilevel" />
<w:lvl w:ilvl="0">
<w:start w:val="1" />
<w:numFmt w:val="decimal" />
<w:lvlText w:val="%1)" />
<w:lvlJc w:val="left" />
<w:pPr>
<w:ind w:left="360" w:hanging="360" />
</w:pPr>
</w:lvl>
<w:lvl w:ilvl="1">
<w:start w:val="1" />
<w:numFmt w:val="lowerLetter" />
<w:lvlText w:val="%2)" />
<w:lvlJc w:val="left" />
<w:pPr>
<w:ind w:left="720" w:hanging="360" />
</w:pPr>
</w:lvl>
</w:abstractNum>
<w:num w:numId="1">
<w:abstractNumId w:val="1"/>
</w:num>
</w:numbering>
The numbering element contains two parts, an abstract numbering definition and the real numbering definition. The abstract numbering definition provides two numbering levels. You are allowed to specify nine levels, with the w:ilvl attribute value ranging from 0 to 8. Each numbering level specifies the value to start numbering with (w:start), a numbering format, the text used for the numbering(the %1 and %2 marks are placeholders for the actual number), and some info on aligning and indenting paragraphs when the numbering is applied to them.
The second part of the main XSLT template creates the main document and calls two separate templates to output the numbering for the source XML file. The XSLT which outputs the main document body looks like the following:
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/3/main">
<w:body>
<xsl:apply-templates select="Order"/>
</w:body>
The template which matches the Order outputs a paragraph which ties to the numbering definition we created above. Basically a nested list in WordProcessingML is a series of not-nested paragraphs, each tying into a specific numbering definition and indentation level.
<xsl:template match="Order">
<w:p xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/3/main">
<w:pPr>
<w:numPr>
<w:ilvl w:val="0" />
<w:numId w:val="1" />
</w:numPr>
</w:pPr>
<w:r>
<w:t>
<xsl:value-of select="name()"/>
</w:t>
</w:r>
</w:p>
<xsl:apply-templates select="OrderItem" />
</xsl:template>
You can download the entire sample XSLT here.