【问题标题】:XSL : Copy Attributes That Match A WhitelistXSL:复制与白名单匹配的属性
【发布时间】:2014-05-23 04:15:42
【问题描述】:

我有一个遵循这个整体模式的 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
    • 所以我可以选择&lt;xsl:copy-of select="@*[(name()!='d') or (name()!='h']"/&gt;,但我的实际属性列表很长。
    • 我确实尝试过查找“is-a-member-of-this-list”类型的函数,但很快就迷路了。
  • 这个答案似乎在讨论白名单,但我显然不够聪明,无法将其应用于属性选择:https://stackoverflow.com/a/5790798/145978

请帮忙

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

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

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

    可以使用document('')函数进行加载和解析,并且可以将其存储在一个变量中以便于引用:

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

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

    然后您可以测试任意属性的名称是否与 $keep 节点集中的任何一个匹配:

    @*[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>
    

    【讨论】:

    • 非常感谢 - 它就像一个魅力。对我来说,关键是变量声明。我对document() 函数一无所知
    【解决方案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>
    

    就是这样,这已经是正确的事情了。

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

    【讨论】:

    • 我只是想知道当白名单很长时这种方法的可行性?
    • @LRE 定义“生存能力”。一个包含 50 个属性的长匹配表达式既不会混淆您的 XSLT 引擎,也不会使整个事情变慢。 (我什至认为这是所有显示的替代方案中最快的方法,即使对于很长的白名单也是如此。)
    • 当然,在撒克逊人中,这样的模板规则中匹配名称会比使用字符串比较执行得更好,因为元素和属性名称的测试被简化为整数比较,并且指定显式名称的模板规则可以通过哈希表找到查找。
    • 公平点,托马拉克。当我说“可行性”时,我是在考虑输入一组非常长的比较与来自helderdarocha 的白名单方法 - 至少在我看来这更具可读性。
    【解决方案3】:

    如果您使用的是 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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-06
      • 1970-01-01
      • 2011-11-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多