【问题标题】:Removing related elements using XSLT 1.0使用 XSLT 1.0 删除相关元素
【发布时间】:2010-04-26 13:56:53
【问题描述】:

我正在尝试从下面的 XML 中删除具有扩展名为“config”的 File 子级的 Component 元素。我已经设法完成了这部分,但我还需要删除与这些组件具有相同“Id”值的匹配 ComponentRef 元素。

<Fragment>
  <DirectoryRef Id="MyWebsite">
    <Component Id="Comp1">
      <File Source="Web.config" />
    </Component>
    <Component Id="Comp2">
      <File Source="Default.aspx" />
    </Component>
  </DirectoryRef>
</Fragment>
<Fragment>
  <ComponentGroup Id="MyWebsite">
    <ComponentRef Id="Comp1" />
    <ComponentRef Id="Comp2" />
  </ComponentGroup>
</Fragment>

基于其他 SO 答案,我提出了以下 XSLT 来删除这些 Component 元素:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" indent="yes" />
    <xsl:template match="Component[File[substring(@Source, string-length(@Source)- string-length('config') + 1) = 'config']]" />
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

不幸的是,这不会删除匹配的 ComponentRef 元素(即那些具有相同“Id”值的元素)。 XSLT 将删除 ID 为“Comp1”的组件,但不会删除 ID 为“Comp1”的 ComponentRef。如何使用 XSLT 1.0 实现这一目标?

【问题讨论】:

    标签: xml xslt xpath xslt-1.0


    【解决方案1】:

    一种相当有效的方法是使用xsl:key 来识别配置组件的ID:

    <?xml version="1.0" encoding="utf-8"?> 
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
        <xsl:output method="xml" indent="yes" />
    
        <xsl:key name="configComponent" 
          match="Component[File/@Source[substring(., 
                   string-length() - string-length('config') + 1) = 'config']]" 
          use="@Id" />
    
        <xsl:template match="Component[key('configComponent', @Id)]" /> 
    
        <xsl:template match="ComponentRef[key('configComponent', @Id)]" /> 
    
        <xsl:template match="@*|node()"> 
            <xsl:copy> 
                <xsl:apply-templates select="@*|node()"/> 
            </xsl:copy> 
        </xsl:template> 
    </xsl:stylesheet>
    

    【讨论】:

    • 是的,我会选择这种方法,但不想使示例复杂化。
    • 我喜欢这种方法,非常优雅:) 因为这似乎是更有效的方法并且在 XPath/XSLT 1.0 上正常工作,所以我会接受这个答案。
    • @pmdarrow - 很高兴你换了 - 我真的没有给答案足够的时间,@markusk 是一个更好的答案
    【解决方案2】:

    这个怎么样?我也对您的原件进行了一些小改动以简化事情(检查@source 属性是否以'config'结尾更简单)。

    <?xml version="1.0" encoding="utf-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:output method="xml" indent="yes" />
        <xsl:template match="Component[substring(@Source, string-length(@Source) - 5) = 'config']" />
        <xsl:template match="ComponentRef[//Component[substring(@Source, string-length(@Source) - 5) = 'config']/@Id = @Id]"/>
            <xsl:template match="@*|node()">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:template>
    

    这有一个模板可以匹配任何 ComponentRef,该模板具有与前面模板匹配的 Component 相同的 Id 属性。一件事 - '//Component' 效率不高。你应该可以用更高效的方法来替换它——我不知道你的 XML 结构

    【讨论】:

    • 感谢您的快速回复!唯一的问题是我仅限于 XPath/XSLT 1.0,它显然没有 fn:ends-with(),这就是我使用子字符串的原因。我用substring() 替换了您对ends-with() 的调用,它运行良好。一个问题:你说//Component 效率低下。从根目录选择(例如/Wix/Fragment/DirectoryRef/)会避免这种低效率吗?
    • 道歉 - 我完全忘记了 XSLT 1.0 中缺少结尾。我会修正这个例子。正如@markusk 下面提到的, xsl:key 将是一种有效的方法,更重要的是实际使用绝对路径(这比我的方法更好)。如果您不止一次查找值,xsl:key 是正确的方法
    • 仍然感谢您的回答。这是我关于 SO 的第一个问题,我对这里的回复质量和速度感到惊讶。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-01
    相关资源
    最近更新 更多