How to test for multiple conditions in previous-si

2019-07-28 19:54发布

I have XML data that was extracted from a legacy Lotus Notes application and that has embedded richtext formatting. I am trying to format each block of text based on attributes that appear in a previous sibling. I have XSLT inspired by this response from @Jayvee but it's not working.

This is the XML:

<?xml version="1.0" encoding="UTF-8"?>
<document>
    <item name="Unordered list">
        <richtext>
            <pardef/>
            <par def="20">
                <run>This is the first </run><run>paragraph of the preamble.</run>
            </par>
            <par>
                <run>This is the second paragraph of the </run><run>preamble.</run>
            </par>
            <pardef id="21" list="unordered"/>
            <par def="21">
                <run>This is the </run><run>first bullet.</run>
            </par>
            <par>
                <run>This is the second </run><run>bullet.</run>
            </par>
            <par def="20">
                <run>This is the first </run><run>paragraph of the conclusion.</run>
            </par>
            <par>
                <run>This is the second paragraph of the </run><run>conclusion.</run>
            </par>
        </richtext>
    </item>
    <item name="Ordered list">
        <richtext>
            <pardef/>
            <par def="20">
                <run>This is the first </run><run>paragraph of the preamble.</run>
            </par>
            <par>
                <run>This is the second paragraph of the </run><run>preamble.</run>
            </par>
            <pardef id="46" list="ordered"/>
            <par def="46">
                <run>This is the </run><run>first numbered item.</run>
            </par>
            <par>
                <run>This is the another </run><run>numbered item.</run>
            </par>
            <par def="20">
                <run>This is the first </run><run>paragraph of the conclusion.</run>
            </par>
            <par>
                <run>This is the second paragraph of the </run><run>conclusion.</run>
            </par>
        </richtext>
    </item>
</document>

This is the desired output:

<html>
  <body>
     <table border="1">
        <tr>
           <td>Unordered list</td>
           <td>
              <p>This is the first paragraph of the preamble.</p>
              <p>This is the second paragraph of the preamble.</p>
              <ul>
                 <li>This is the first bullet.</li>
                 <li>This is the second bullet.</li>
              </ul>
              <p>This is the first paragraph of the conclusion.</p>
              <p>This is the second paragraph of the conclusion.</p>
           </td>
        </tr>
        <tr>
           <td>Ordered list</td>
           <td>
              <p>This is the first paragraph of the preamble.</p>
              <p>This is the second paragraph of the preamble.</p>
              <ol>
                 <li>This is the first numbered item.</li>
                 <li>This is the another numbered item.</li>
              </ol>
              <p>This is the first paragraph of the conclusion.</p>
              <p>This is the second paragraph of the conclusion.</p>
           </td>
        </tr>
     </table>
  </body>

This is the XSLT:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output indent="yes"/>

    <xsl:template match="/*">
        <html>
            <body>
                <table border="1">
                        <xsl:apply-templates/>
                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="item">
        <tr>
            <td><xsl:value-of select="@name"/></td>
            <td>
                <xsl:apply-templates/>
            </td>
        </tr>
    </xsl:template>

    <xsl:template match="par">

        <xsl:choose>
        <xsl:when test="preceding-sibling::pardef[@list] = 'unordered' and preceding-sibling::par[@def][1][@def] != preceding-sibling::pardef[@id]"><xsl:text disable-output-escaping="yes">&lt;/ul&gt;</xsl:text></xsl:when>
        <xsl:when test="preceding-sibling::pardef[@list] = 'ordered' and preceding-sibling::par[@def][1][@def] != preceding-sibling::pardef[@id]"><xsl:text disable-output-escaping="yes">&lt;/ol&gt;</xsl:text></xsl:when>  
        </xsl:choose>

        <xsl:choose>

            <xsl:when test="@def=preceding-sibling::pardef[@id] or (not(@def) and preceding-sibling::par[@def][1][@def=preceding-sibling::pardef[@id]])">    
                <xsl:choose>
                    <xsl:when test="preceding-sibling::pardef[@list] = 'unordered' and preceding-sibling::par[@def][1][@def] = preceding-sibling::pardef[@id]"><xsl:text disable-output-escaping="yes">&lt;ul&gt;</xsl:text></xsl:when>
                    <xsl:when test="preceding-sibling::pardef[@list] = 'ordered' and preceding-sibling::par[@def][1][@def] = preceding-sibling::pardef[@id]"><xsl:text disable-output-escaping="yes">&lt;ol&gt;</xsl:text></xsl:when>  
                </xsl:choose>
                <li>
                    <xsl:for-each select="run">
                        <xsl:value-of select="text()" separator=""/>
                    </xsl:for-each>
                </li>   
            </xsl:when>

            <xsl:when test="not(@def=preceding-sibling::pardef[@id])">
                <p>
                    <xsl:for-each select="run">
                        <xsl:value-of select="text()" separator=""/>
                    </xsl:for-each>
                </p>    
            </xsl:when>     

        </xsl:choose>   

    </xsl:template>
</xsl:stylesheet>

1条回答
老娘就宠你
2楼-- · 2019-07-28 20:34

The approach in the previous question use disable-output-escaping to output the start and end tags, which is not an ideal approach.

Instead, consider using a key to group together par elements by the first preceding par element with a def attribute

<xsl:key name="pars" 
         match="par[not(@def)]" 
         use="generate-id(preceding-sibling::par[@def][1])" />

And, assuming you are matched on a par element a def attribute, you can use the key like so:

<xsl:variable name="group" select="self::* | key('pars', generate-id())" />

To work out whether to wrap the group in an ul or ol tag, you can possibly get the list type as follows:

<xsl:variable name="listType" select="preceding-sibling::*[1][self::pardef]/@list" />

You can then test this to determine whether to wrap the group in a list tag.

Try this XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
    <xsl:output indent="yes"/>

    <xsl:key name="pars" match="par[not(@def)]" use="generate-id(preceding-sibling::par[@def][1])" />

    <xsl:template match="/*">
        <html>
            <body>
                <table border="1">
                        <xsl:apply-templates />
                </table>
            </body>
        </html>
    </xsl:template>

    <xsl:template match="item">
        <tr>
            <td><xsl:value-of select="@name"/></td>
            <td>
                <xsl:apply-templates select="richtext/par[@def]" />
            </td>
        </tr>
    </xsl:template>

    <xsl:template match="par[@def]">
        <xsl:variable name="listType" select="preceding-sibling::*[1][self::pardef]/@list" />
        <xsl:variable name="group" select="self::* | key('pars', generate-id())" />
        <xsl:choose>
            <xsl:when test="$listType = 'unordered'">    
                <ol>
                    <xsl:apply-templates select="$group" mode="list"/>
                </ol>
            </xsl:when>
            <xsl:when test="$listType = 'ordered'">    
                <ul>
                    <xsl:apply-templates select="$group"  mode="list"/>
                </ul>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="$group" mode="para" />   
            </xsl:otherwise>     
        </xsl:choose>   
    </xsl:template>

    <xsl:template match="par" mode="list">
        <li>
            <xsl:value-of select="run" separator=""/>
        </li>  
    </xsl:template>

    <xsl:template match="par" mode="para">
        <para>
            <xsl:value-of select="run" separator=""/>
        </para>  
    </xsl:template>
</xsl:stylesheet>
查看更多
登录 后发表回答