Search code examples
xmlxsltadobe-indesign

Using XSLT to edit tags in a multilevel bulleted list


The following is some simple XML with a multilevel unordered list that I would like to import into Adobe InDesign:

<?xml version="1.0" encoding="utf-8" standalone="yes"?> 
<STORY> 
<Headline>XML test</Headline> 
<Standfirst><p>Standfirst</p></Standfirst> 
<Story_text>
<h1>Heading 1</h1> 
<p>para text</p>
<ul>
<li>
    level 1
    <ul>
        <li>
            level 2
            <ul>
                <li>
                    level 3
                    <ul>
                        <li>
                            level 4
                        </li>
                    </ul>
                </li>
            </ul>
        </li>
    </ul>
</li>
<li>
    level 1
    <ul>
        <li>
            level 2
        </li>
    </ul>
</li>
<li>
    level 1
</li>
</ul>
</Story_text> 
</STORY>

InDesign allows you to map its paragraph styles to XML tags. (e.g. <h1> can be assigned to my Header 1 paragraph style). This is fine for the most part; however, my problem arises with multilevel/nested bulleted lists.

To do multi-level bullets in InDesign, I have separate paragraph styles for each level of bullets. Below is how I'd like the unordered list to appear in InDesign, with the relevant paragraph styles in brackets.

  • level 1 (TEXT: bullet 1)
    • level 2 (TEXT: bullet 2)
      • level 3 (TEXT: bullet 3)
        • level 4 (TEXT: bullet 4)
  • level 1 (TEXT: bullet 1)
    • level 2 (TEXT: bullet 2)
  • level 1 (TEXT: bullet 1)

The problem is InDesign has no way of distinguishing between multilevel bullets. As you would expect, if I assign <li> to one of the bullet paragraph styles, all the bullets will become that level, same if I assign the style to <ul>.

I would like to use XSLT to output the following:

<?xml version="1.0" encoding="utf-8" standalone="yes"?> 
<STORY> 
<Headline>XML test</Headline> 
<Standfirst><p>Standfirst</p></Standfirst> 
<Story_text>
    <h1>Heading 1</h1> 
    <p>para text</p>
<ul>
<li1>
    level 1
    <ul>
        <li2>
            level 2
            <ul>
                <li3>
                    level 3
                    <ul>
                        <li4>
                            level 4
                        </li4>
                    </ul>
                </li3>
            </ul>
        </li2>
    </ul>
</li1>
<li1>
    level 1
    <ul>
        <li2>
            level 2
        </li2>
    </ul>
</li1>
<li1>
    level 1
</li1>
</ul>
</Story_text> 
</STORY>

This will allow me to map the bullet level to the appropriate paragraph style in InDesign (e.g. <li1> can be mapped to TEXT: bullet 1, <li2> mapped to TEXT: bullet 2 and so on). I am quite new to XML and XSLT, so any help would be massively appreciated.


Solution

  • You should first start off with the XSLT identity template to copy all elements across unchanged...

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    

    Then all you need is to add an overriding template that matches li and outputs a new element with the number of ancestors in the name instead.

    <xsl:template match="li">
      <xsl:element name="li{count(ancestor::li) + 1}">
            <xsl:apply-templates select="@*|node()"/>
      </xsl:element>
    </xsl:template>
    

    Note the curly braces indicate an Attribute Value Template, which indicate an expression to be evaluated, rather than output literally.

    Try this XSLT

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
        <xsl:output method="xml" indent="no" />
    
        <xsl:template match="li">
          <xsl:element name="li{count(ancestor::li) + 1}">
                <xsl:apply-templates select="@*|node()"/>
          </xsl:element>
        </xsl:template>
    
        <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>