XSLT - 根据地图从模板中选择节点

时间:2016-06-06 18:06:47

标签: xml xslt xslt-2.0

这是我为解释目的编写的示例xml内容,

<doc>
    <array>
        <?Table_type Small?>
        <table>
            <data>text</data>
        </table>
    </array>

    <array>
        <?Table_type Medium?>
        <table>
            <data>text</data>
        </table>
    </array>

    <array>
        <?Table_type Large?>
        <table>
            <data>text</data>
        </table>
    </array>
</doc>

在这里,您可以识别3种类型的表,包含处理指令值Small,Medium和Large的表。 (在实际文档中有更多的表类型)

我想要做的是,通过检查表类型(使用处理指令识别)将size属性添加到<data>个节点。

由于原始文档中存在大量的表类型,我使用以下XSL方法来执行此任务,

<xsl:variable name="table.type.mapping">
        <map parent='Small' mapto='10'/>
        <map parent='Medium' mapto='20'/>
        <map parent='Large' mapto='30'/>
    </xsl:variable>

    <xsl:template match="data[ancestor::array/processing-instruction('Table_type')=$table.type.mapping/map[@parent=ancestor::array/processing-instruction('Table_type')]/@parent]">
        <data size="{$table.type.mapping/map[@parent=ancestor::array/processing-instruction('Table_type')]/@mapto}">
            <xsl:apply-templates/>
        </data>
    </xsl:template>

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

但它没有从地图中读取相应的属性值并放在相应的<data>节点中。

我的预期输出是,

<doc>
    <array>
        <?Table_type Small?>
        <table>
            <data size="10">text</data>
        </table>
    </array>

    <array>
        <?Table_type Medium?>
        <table>
            <data size="20">text</data>
        </table>
    </array>

    <array>
        <?Table_type Large?>
        <table>
            <data size="30">text</data>
        </table>
    </array>
</doc>

是否有任何建议我如何更改模板以从地图中读取正确的值并将正确的属性添加到<data>节点?

3 个答案:

答案 0 :(得分:3)

使用进行查找可以非常方便(并且有效)地完成此操作:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:variable name="table.type.mapping">
    <map parent='Small' mapto='10'/>
    <map parent='Medium' mapto='20'/>
    <map parent='Large' mapto='30'/>
</xsl:variable>

<xsl:key name="size" match="map" use="@parent" />

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

<xsl:template match="data">
    <data size="{key('size', ancestor::array/processing-instruction('Table_type'), $table.type.mapping)/@mapto}">
        <xsl:apply-templates/>
    </data>
</xsl:template>

</xsl:stylesheet>

答案 1 :(得分:1)

您可能不需要在匹配中使用复杂的xpath表达式,尤其是如果所有data元素在它们之前都有有效的处理指令。

主要问题是AVT用于计算size属性。它正在寻找map元素的祖先,而不是当前的data元素。应该是这个......

<data size="{$table.type.mapping/map[@parent=current()/ancestor::array/processing-instruction('Table_type')]/@mapto}">

试试这个XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="table.type.mapping">
        <map parent='Small' mapto='10'/>
        <map parent='Medium' mapto='20'/>
        <map parent='Large' mapto='30'/>
    </xsl:variable>

    <xsl:template match="data">
        <data size="{$table.type.mapping/map[@parent=current()/ancestor::array/processing-instruction('Table_type')]/@mapto}">
            <xsl:apply-templates/>
        </data>
    </xsl:template>

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

如果某些data元素没有相应的处理指令,请尝试使用此XSLT

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:variable name="table.type.mapping">
        <map parent='Small' mapto='10'/>
        <map parent='Medium' mapto='20'/>
        <map parent='Large' mapto='30'/>
    </xsl:variable>

    <xsl:template match="data">
        <data>
            <xsl:variable name="size" select="$table.type.mapping/map[@parent=current()/ancestor::array/processing-instruction('Table_type')]/@mapto" />
            <xsl:if test="$size">
                <xsl:attribute name="size" select="$size" />
            </xsl:if>
            <xsl:apply-templates/>
        </data>
    </xsl:template>

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

答案 2 :(得分:0)

你一直陷入Resulting tree fragment的陷阱,这是XSLT-1.0的一个严重问题。

避免这种情况的一种方法是将您的<xsl:variable>转换为XSLT文件中的数据岛。这个数据岛需要一个名为var的名称空间,必须使用exclude-result-prefixes指令从结果中明确排除。

此外,我确实使用了两个变量来避免XML和XSLT命名空间之间的命名空间冲突。

仅供参考:document('')/xsl:stylesheet确实引用当前XSLT文档的XML根节点。

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

  <var:tabletypemapping>
        <map parent='Small' mapto='10'/>
        <map parent='Medium' mapto='20'/>
        <map parent='Large' mapto='30'/>
  </var:tabletypemapping>

  <xsl:template match="data">
    <xsl:variable name="procIns" select="ancestor::array/processing-instruction('Table_type')" />
    <xsl:variable name="mapIns"  select="document('')/xsl:stylesheet/var:tabletypemapping/map[@parent = $procIns]/@mapto" />
    <data size="{$mapIns}">
      <xsl:apply-templates/>
    </data>
  </xsl:template>

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

</xsl:stylesheet>

输出为:

<?xml version="1.0"?>
<doc>
    <array>
        <?Table_type Small?>
        <table>
            <data size="10">text</data>
        </table>
    </array>

    <array>
        <?Table_type Medium?>
        <table>
            <data size="20">text</data>
        </table>
    </array>

    <array>
        <?Table_type Large?>
        <table>
            <data size="30">text</data>
        </table>
    </array>
</doc>