使用 XML 样式表删除与另一个文件中的内容匹配的元素

Use XML Stylesheet to remove elements which match content in another file

我想像这样转换 xml 文件:

(input.xml)

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef>
            <Component Id="c1">
                <File Source="!(PublishDir)\FileA" />
            </Component>
            <Component Id="c2">
                <File Source="!(PublishDir)\FileB" />
            </Component>
            <Component Id="c3">
                <File Source="!(PublishDir)\FileC" />
            </Component>
            <Component Id="c4">
                <File Source="!(PublishDir)\FileD" />
            </Component>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup>
            <ComponentRef Id="c1" />
            <ComponentRef Id="c2" />
            <ComponentRef Id="c3" />
            <ComponentRef Id="c4" />
        </ComponentGroup>
    </Fragment>
</Wix>

这是热量的简化输出,wix(Windows安装程序XML)收割机。但这应该没什么区别。

从原来的 xml 中删除一些不需要的文件(包括 Component 和 ComponentRef 标签)。其中一些文件是预先知道的 (FileA),一些文件名在这样的文件中:

(filelist.xml)

<Files>
  <File>FileB</File>
  <File>FileC</File>
</Files>

结果应如下所示:

(output.xml)

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef>
            <Component Id="c4">
                <File Source="!(PublishDir)\FileD" />
            </Component>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup>
            <ComponentRef Id="c4" />
        </ComponentGroup>
    </Fragment>
</Wix>

删除事先已知的文件很简单,这是我目前所掌握的:

(stylesheet.xsl)

<xsl:stylesheet
    version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
    xmlns="http://schemas.microsoft.com/wix/2006/wi">

  <!-- identity transform -->
  <xsl:template match="@*|*">
    <xsl:copy>
      <xsl:apply-templates select="@*" />
      <xsl:apply-templates select="*" />
    </xsl:copy>
  </xsl:template>
  <xsl:output method="xml" indent="yes" />

  <!-- remove files -->
  <xsl:key name="file-search" match="wix:Component[substring(wix:File/@Source, string-length(wix:File/@Source) - 4) = 'FileA']" use="@Id"/>
  <xsl:template match="wix:Component[key('file-search', @Id)]" />
  <xsl:template match="wix:ComponentRef[key('file-search', @Id)]" />
</xsl:stylesheet>

但是如何从“filelist.xml”中删除文件呢?这是我尝试过的方法,但这不起作用(匹配中不允许使用变量)

<xsl:variable name="filelist" select="document('filelist.xml')"/>
<xsl:key name="file-search" match="wix:Component[contains($filelist, substring-after(wix:File/@Source, '\'))]" use="@Id"/>

XSLT 1.0 解决方案更可取,因为工具支持它,但我也可以使用带有一些额外连接的 XSLT 2.0。

在 XSLT 1.0 中执行此操作时面临的主要障碍是 (a) 键不能跨文档工作,以及 (b) 不能在匹配模式中使用变量。

也许你可以这样做:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
exclude-result-prefixes="wix">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="component-by-filename" match="wix:Component" use="substring-after(wix:File/@Source, '\')" />

<xsl:variable name="exclude-ids" select="key('component-by-filename', 'FileA')/@Id | key('component-by-filename', document('filelist.xml')/Files/File)/@Id "/>

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

<xsl:template match="wix:DirectoryRef | wix:ComponentGroup">
    <xsl:copy>
        <xsl:apply-templates select="*[not(@Id = $exclude-ids)]"/>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>