简体   繁体   中英

Process XSLT output with XSLT in the same document, flat data to tree nodeset

Let me start with an example and work my way to the question. Here is a very simple and clean XML data:

<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="flatdata.xsl"?>

<db>
<item>
    <parent></parent>
    <id>1</id>
    <title>lorem ipsum</title>
</item>

<item>
    <parent></parent>
    <id>2</id>
    <title>dolor sit amet</title>
</item>

<item>
    <parent>1</parent>
    <id>3</id>
    <title>consectetur adipiscing elit.</title>
</item>

<item>
    <parent>1</parent>
    <id>4</id>
    <title>Nunc varius tempus sem et fringilla</title>
</item>

<item>
    <parent>2</parent>
    <id>5</id>
    <title>Aenean egestas</title>
</item>

<item>
    <parent>2</parent>
    <id>6</id>
    <title>turpis vel placerat suscipit</title>
</item>

<item>
    <parent>5</parent>
    <id>7</id>
    <title>ligula nulla consequat justo</title>
</item>
</db>

This is a flat database of items, with a tree structure lurking within, given by <parent> relationship.

An XSLT that transforms this to a nested structure to reveal the tree-relationship of the item s is pretty straightforward (I did my homework):

<xsl:stylesheet version="1.0"   xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="/">
    <html>
        <body>
            <xsl:apply-templates select="db"/>
        </body>
    </html>
</xsl:template>

<xsl:template match="db">
    <ul>
        <xsl:apply-templates select="item[parent='']"/>
    </ul>
</xsl:template>

<xsl:template match="item">
    <xsl:variable name="vId" select="id"/>
    <xsl:variable name="vChildren" select="../item[parent=$vId]"/>
    <li>
        <b><xsl:value-of select="concat(id,': ')"/></b> <xsl:value-of select="title"/>
        <xsl:if test="$vChildren">
            <ul>
                <xsl:apply-templates select="$vChildren"/>
            </ul>
        </xsl:if>
    </li>
</xsl:template>
</xsl:stylesheet>

which yields the following output:

<ul>
<li>
    <b>1: </b>lorem ipsum
    <ul>
        <li><b>3: </b>consectetur adipiscing elit.</li>
        <li><b>4: </b>Nunc varius tempus sem et fringilla</li>
    </ul>
</li>
<li>
    <b>2: </b>dolor sit amet
    <ul>
        <li>
            <b>5: </b>Aenean egestas
            <ul>
                <li>
                    <b>7: </b>ligula nulla consequat justo
                </li>
            </ul>
        </li>
        <li>
            <b>6: </b>turpis vel placerat suscipit
        </li>
    </ul>
</li>
</ul>

The situation is that this nested (tree) structure is more convenient to process with XSLT for my purposes. I can use straightforward XPath etc to analyze which item has how many descendants and so on. The XML data I receive from the source is flat, nothing I can do much about that.

So, how can one

  1. identify and create the tree structure is some format (doesn't have to be HTML of course, it would inevitably be some internal format, or nodeset?) and
  2. further process that tree with XSLT itself, on the same stylesheet?

I am trying to display the flatdata base results via XSLT on modern browsers, so I guess that means I am stuck with 1.0 (right?).

You can create a global variable <xsl:variable name="rtf"><xsl:apply-templates/></xsl:variable> in any version of XSLT, however in XSLT 1.0 you would then have a result tree fragment which you can only output with xsl:copy-of but not navigate with XPath unless you use an extension function like exsl:node-set in <xsl:variable name="ns" select="exsl:node-set($rtf)" xmlns:exsl="http://exslt.org/common"/> . However there is where inside browsers the trouble begins as IE and Edge don't support exsl:node-set , only a proprietary msxsl:node-set in a different namespace ( xmlns:msxsl="urn:schemas-microsoft-com:xslt" ). Using an extension function it is possible to trick IE into recognizing exsl:node-set but that approach does not work in Edge. So in cross-browser XSLT 1.0 you are left with using xsl:choose/xsl:when/xsl:if to test with eg function-available('exsl:node-set') which function is supported to then convert the result tree fragment into a node set.

As an alternative you could look into Saxon-CE or Saxon-JS bringing XSLT 2.0 to browsers where the restriction about result tree fragments does not exist.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM