XSL:复制与白名单匹配的属性

时间:2014-05-23 04:15:43

标签: xml xslt

我有一个遵循这个整体模式的xml文档:

<A b="c" d="e" f="g" h="i">
  <!-- plenty of children -->
</A>

我想复制A节点只包含其中的一些属性:

<A b="c" f="g">
  <!-- some of the children -->
</A>

这里的其他答案已接近解决我的挑战,但还不够:

  • 这个答案给了我一个可以工作但很长的解决方案:https://stackoverflow.com/a/672962/145978
    • 所以我可以使用<xsl:copy-of select="@*[(name()!='d') or (name()!='h']"/>,但我的实际属性列表很长。
    • 我确实尝试过找一个'is-a-member-of-list-type'类型的功能但很快就迷路了。
  • 这个答案似乎讨论了白名单,但我显然不够聪明,无法将其应用于属性选择:https://stackoverflow.com/a/5790798/145978

请帮忙

3 个答案:

答案 0 :(得分:3)

您链接的白名单解决方案使用包含应保留的元素列表的嵌入式文档。您的属性可以有类似的属性:

<myns:whitelist>
    <keep>b</keep>
    <keep>f</keep>
</myns:whitelist>

可以使用document('')函数加载和解析它,您可以将其存储在变量中,以便更容易引用它:

<xsl:variable name="keep" select="document('')/*/myns:whitelist/keep"/>

现在$keep变量包含列表中所有属性的名称。星号表示<xsl:stylesheet>元素,因为传递给document()的参数是一个空字符串,这会导致它从当前文档加载。

然后,您可以测试任意atrributes的名称是否与$keep node-set中的任何名称匹配:

@*[name()=$keep]

使用身份转换复制的其他人。

以下是您提供的示例的完整样式表:

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

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>

    <myns:whitelist>
        <keep>b</keep>
        <keep>f</keep>
    </myns:whitelist>

    <xsl:variable name="keep" select="document('')/*/myns:whitelist/keep"/>

    <xsl:template match="A">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*[name()=$keep]"/>
        </xsl:copy>
    </xsl:template>

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

</xsl:stylesheet>

答案 1 :(得分:2)

通常,如果您想从输入中删除某些内容,请为要删除的内容编写一个空模板:

<!-- drop every attribute of <A> ... -->
<xsl:template match="A/@*" />

另一个非空的模板,用于保存您的内容:

<!-- ... except @b and @f -->
<xsl:template match="A/@b | A/@f">
  <xsl:copy-of select="." />
</xsl:template>

然后只需正常应用模板:

<xsl:template match="A">
  <xsl:copy>
    <xsl:apply-templates select="@*" />
    <!-- other output -->
  </xsl:copy>
</xsl:template>

总而言之,这已经做对了。

提示:如果您的样式表中有一个身份模板,并且不需要对<A>进行其他更改,那么您甚至不需要第三个模板。

答案 2 :(得分:1)

如果您正在使用XSLT 2.0,则可以使用序列。如果您将其放在xsl:param而不是xsl:variable中,则可以在运行时定义白名单(如果需要)。

示例:

XSLT 2.0

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

    <xsl:param name="whitelist" select="('b','f')"/>

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

    <xsl:template match="A">
        <xsl:copy>
            <xsl:apply-templates select="@*[name()=$whitelist]|node()"/>
        </xsl:copy>        
    </xsl:template>

</xsl:stylesheet>