Skip processing of already processed nodes

2019-08-23 23:22发布

Is there a way to avoid processing of already processed nodes?

Input XML

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <node1>node1.1</node1>
    <node2>node2.1</node2>
    <node2>node2.2</node2>
    <node1>node1.2</node1>
</root>

XSL

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

    <xsl:template match="root">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="node1">
        [Node1]:<xsl:value-of select="."></xsl:value-of>
        <xsl:apply-templates select="following-sibling::node2"/>
        [End node1]
    </xsl:template>

    <xsl:template match="node2">
            [Node2]:<xsl:value-of select="."></xsl:value-of>
    </xsl:template>

</xsl:stylesheet>

Output

<?xml version="1.0" encoding="UTF-8"?>

        [Node1]:node1.1
            [Node2]:node2.1
            [Node2]:node2.2
        [End node1]

            [Node2]:node2.1

            [Node2]:node2.2

        [Node1]:node1.2
        [End node1]

As you can see template <xsl:template match="node2"> is applied twice for every node2 element - one time from node1 template and second time when XSLT processor is transforming node2 element.

Is there any solution to avoid applying of xsl:template match="node2" second time? I need to stop processing of node2 when I just processed it in template for node1.

Important This example is just an emulation of more complex use case. This means that we have additonal limitation - we can't modify template for root element processing.

I want to know if there is any way to stop processing of elements or move processing to some other elements.

标签: xslt
3条回答
Anthone
2楼-- · 2019-08-23 23:34

You can use mode to name the template to use.

You can create an empty catch-all node that will output nothing, taking care of apply-templates calls that have no select.

The following stylesheet outputs what you need:

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

    <xsl:template match="root">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="node1">
        [Node1]:<xsl:value-of select="."></xsl:value-of>
        <xsl:apply-templates select="following-sibling::node2" mode="fromNode1"/>
        [End node1]
    </xsl:template>

    <xsl:template match="node2" mode="fromNode1">
            [Node2]:<xsl:value-of select="."></xsl:value-of>
    </xsl:template>

    <xsl:template match="node2"></xsl:template>
</xsl:stylesheet>

Note the empty modeless template at the end, and the added mode attribute on the template and the calling apply-templates.

查看更多
狗以群分
3楼-- · 2019-08-23 23:42

XSLT doesn't track state and every apply-templates, for-each, etc will potentially produce "redundant" results, but this is entirely a problem in the design of the style-sheet - if you don't want to "process" a node more than once you need to change the appropriate templates and selects so it doesn't get handled more than once.

That would be fairly trivial for your example, but you said that your example isn't very representitive so I'd suggest you post something more complete if you're having problems.

查看更多
Root(大扎)
4楼-- · 2019-08-23 23:45

This stylesheet:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>
    <xsl:key name="kNode2ByPrecedingNode1" match="node2"
             use="generate-id(preceding-sibling::node1)"/>
    <xsl:template match="root">
        <xsl:apply-templates select="node1"/>
    </xsl:template>
    <xsl:template match="node1">
        <xsl:value-of select="concat('[Node1]: ',.,'&#xA;')"/>
        <xsl:apply-templates select="key('kNode2ByPrecedingNode1',
                                         generate-id())"/>
        <xsl:text>[End node1]&#xA;</xsl:text>
    </xsl:template>
    <xsl:template match="node2">
        <xsl:value-of select="concat('&#x9;[Node2]: ',.,'&#xA;')"/>
    </xsl:template>
</xsl:stylesheet>

Output:

[Node1]: node1.1
    [Node2]: node2.1
    [Node2]: node2.2
[End node1]
[Node1]: node1.2
[End node1]

Note: Two problems: you process node2 more than once, from root rule with applying templates to all node children, and from node1 rule; plus your following-sibling::node2 expression doesn't distinguish wich node2 follows some node1.

Edit: If you can't modify how root rule apply templates, then you would need modes for process and skip proccess:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="text"/>
    <xsl:key name="kNode2ByPrecedingNode1" match="node2"
             use="generate-id(preceding-sibling::node1)"/>
    <xsl:template match="root">
        <xsl:apply-templates/>
    </xsl:template>
    <xsl:template match="node1">
        <xsl:value-of select="concat('[Node1]: ',.,'&#xA;')"/>
        <xsl:apply-templates select="key('kNode2ByPrecedingNode1',
                                         generate-id())"
                             mode="output"/>
        <xsl:text>[End node1]&#xA;</xsl:text>
    </xsl:template>
    <xsl:template match="node2"/>
    <xsl:template match="node2" mode="output">
        <xsl:value-of select="concat('&#x9;[Node2]: ',.,'&#xA;')"/>
    </xsl:template>
</xsl:stylesheet>
查看更多
登录 后发表回答