使用 XSLT 1.0 根据引用的 XML 文件中的元素删除元素 (WXS components/files/etc)
Using XSLT 1.0 to remove elements (WXS components/files/etc) based on element in referenced XML file
(抱歉,这可能有点长)
我有一个由 WiX 的热实用程序生成的 WXS 文件。我正在尝试使用(现有的)exclusions.xslt 文件对其进行修改,以根据另一个 XML 文件的内容自动排除某些组件(我将调用这个parts.xml)。 xslt文件目前用于从安装程序中删除一些components/files/directories,但对于一个相对静态的列表。
我正在转换的 WXS 文件有 File 元素,所有元素都有一个 "Source" 属性,这是删除元素所应用的标准。
我的目标是从我的 XSLT 样式表中读取 parts.xml 文件,并使用它从我的 Wix 安装程序中排除一些元素。我们目前排除元素的方式是:
我们首先将每个元素(整个源xml)复制过来。像这样:
<!-- Copy all attributes and elements to the output. -->
<xsl:template match="@*|*">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
然后我们使用xsl:key指令来标记某些项目,像这样:
<xsl:key name="removals" match="wix:Component[wix:File[contains(@Source, ')\fileToBeRemoved1.txt')]]" use="@Id" />
<xsl:key name="removals" match="wix:Component[wix:File[contains(@Source, ')\fileToBeRemoved2.exe')]]" use="@Id" />
然后我们删除它们:
<xsl:template match="wix:Component[key('removals', @Id)]" />
<xsl:template match="wix:Directory[key('removals', @Id)]" />
<xsl:template match="wix:ComponentRef[key('removals', @Id)]" />
我想我已经到了能够将 parts.xml 文件读入样式表的地步:
<!-- Adapted from and http://geekswithblogs.net/Erik/archive/2008/04/01/120915.aspx-->
<xsl:param name="srcroot" />
<xsl:variable name="filename">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="concat($srcroot, 'foo/parts.xml')" />
<xsl:with-param name="replace" select="'\'" />
<xsl:with-param name="by" select="'/'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="partsfile" select="document(concat('file://', $filename))" />
<xsl:variable name="myOutputFiles">
<xsl:call-template name="str:tokenize">
<xsl:with-param name="string" select="normalize-space($partsfile/xsi:Apple/xsi:Banana[@Name = 'my_sdk']/xsi:Carrot/xsi:Fig[@Directory = 'OutputFiles'])"/>
</xsl:call-template>
</xsl:variable>
我从 here 得到了 str:tokenize。我想我设置 myOutputFiles 的方式存在问题,尤其是命名空间问题,但我不完全确定如何正确执行此操作。我已经将 xsi 命名空间添加到我的顶级样式表节点,但是当我打印出值时这似乎没有产生任何结果:
<xsl:message terminate="yes">
<xsl:text>INFO: </xsl:text>
<xsl:text>
</xsl:text>
<xsl:copy-of select="$myOutputFiles"/>
<xsl:text>
</xsl:text>
</xsl:message>
我的下一步是以某种方式对返回的每个标记调用 指令,以便我上面编写的三个模板指令将删除必要的项目。但是由于 必须是样式表中的顶级元素,所以我不确定我该怎么做。
最终,一旦我得到标记,我想遍历它们并通过以类似于上面的方式标记它们来排除它们:
<xsl:key name="removals" match="wix:Component[wix:File[contains(@Source, ')\{PUT STRING FROM TOKEN HERE}')]]" use="@Id" />
然后它们将被上面的模板说明删除。 ')\' 前缀对于添加到令牌字符串很重要。 这就是它知道只从 INSTALLDIRECTORY 节点而不是任何子目录中删除组件的方式。
我怎样才能完成这个任务?非常感谢任何帮助!
编辑:
我使用 MSXSL 作为我的处理器。我不相信它本身可以做 str:tokenize,但我从 here 复制了模板并将其包含在我的样式表底部以供使用。除了消除 标签外,它似乎工作正常。如果我用 string="'file1 file2 file3'" 调用它并按照我上面打印的方式打印出来,它会输出 "file1file2file3" 到控制台。
这是转换前的一些示例输入 XML:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLDIRECTORY">
<Component Id="cmp53284C0E6C6EC93D8D9DE8E3703596E4" Guid="*">
<File Id="filBD973F0EAED6A0B34BE693B053368769" KeyPath="yes" Source="$(env.srcDir)\file1.txt" />
</Component>
<Component Id="cmp81302C0E6C6EC93D877778E270358FFE" Guid="*">
<File Id="filAA273F0EAED6A0B34BE693B053A129EA" KeyPath="yes" Source="$(env.srcDir)\file2.exe" />
</Component>
<Component Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" Guid="*">
<File Id="filF5C36F42ADA8B3DD927354B5AB666898" KeyPath="yes" Source="$(env.srcDir)\thisWillStay.txt" />
</Component>
<Component Id="cmpABC123AE6C6EC93D8D9DE8E370BEEF39" Guid="*">
<File Id="fil72EA34F0EAED6A0B34BE693B05334421" KeyPath="yes" Source="$(env.srcDir)\Some.File.dll" />
</Component>
<Directory Id="dir2A411B40F7C80649B57155F53DD7D136" Name="ThisWillStay">
<Component Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" Guid="*">
<File Id="fil908CF12B8DD2E95A77D7611874EC3892" KeyPath="yes" Source="$(env.srcDir)\ThisWillStay\file1.txt" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="DeliveredFiles">
<ComponentRef Id="cmp53284C0E6C6EC93D8D9DE8E3703596E4" />
<ComponentRef Id="cmp81302C0E6C6EC93D877778E270358FFE" />
<ComponentRef Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" />
<ComponentRef Id="cmpABC123AE6C6EC93D8D9DE8E370BEEF39" />
<ComponentRef Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" />
</ComponentGroup>
</Fragment>
</Wix>
引用的 xml 文件 (parts.xml) 看起来像(通往 Fig 的节点树很重要,但它周围还有其他元素):
<?xml version="1.0" encoding="utf-8"?>
<Apple xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../foo/schema.xsd" >
<!-- ... -->
<Banana Name="my_sdk" SomeAttribute="${srcroot}some/path/to/file.txt">
<Carrot>
<Dill SomeOtherAttribute="SomeIdentifier"/>
<Fig Directory="OutputFiles">
foo/bar/file1.txt
foo/bar/file2.exe
foo/bar/Some.File.dll
<!-- ... -->
</Fig>
</Carrot>
</Banana>
<!-- ... -->
</Apple>
图中指定的文件与要从 WXS 文件中删除 的组件相关(尽管它们在此处的路径并不重要)。此参考文件基于 XML Schema 实例(schema.xsd 文件),类似于:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Apple">
<!-- (various xs:elements defined for elements used in other parts file) -->
</xs:element>
</xs:schema>
和预期输出XML:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLDIRECTORY">
<Component Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" Guid="*">
<File Id="filF5C36F42ADA8B3DD927354B5AB666898" KeyPath="yes" Source="$(env.srcDir)\thisWillStay.txt" />
</Component>
<Directory Id="dir2A411B40F7C80649B57155F53DD7D136" Name="ThisWillStay">
<Component Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" Guid="*">
<File Id="fil908CF12B8DD2E95A77D7611874EC3892" KeyPath="yes" Source="$(env.srcDir)\ThisWillStay\file1.txt" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="DeliveredFiles">
<ComponentRef Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" />
<ComponentRef Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" />
</ComponentGroup>
</Fragment>
</Wix>
file1.txt 组件现在从第一个片段中消失了,相应的 ComponentRef 在第二个片段中也消失了。 (路径对于参考文件无关紧要。它应该只使用文件名来排除 INSTALLDIRECTORY 节点中的文件,而不是源文件的任何子目录。我想我可以只使用 "substring-after-last" like this).
编辑2:
如果有帮助,这里有一些我从各种来源获取的模板,我打算使用它们:
<!--
Adapted from:
-->
<xsl:template name="substring-after-last">
<xsl:param name="haystack" />
<xsl:param name="needle" />
<xsl:choose>
<xsl:when test="contains($haystack, $needle)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="haystack"
select="substring-after($haystack, $needle)" />
<xsl:with-param name="needle" select="$needle" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$haystack" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Tokenizing: http://exslt.org/str/functions/tokenize/index.html -->
<!-- Code from: http://exslt.org/str/functions/tokenize/str.tokenize.template.xsl -->
<xsl:template name="str:tokenize">
<xsl:param name="string" select="''"/>
<xsl:param name="delimiters" select="' '"/>
<xsl:choose>
<xsl:when test="not($string)"/>
<xsl:when test="not($delimiters)">
<xsl:call-template name="str:_tokenize-characters">
<xsl:with-param name="string" select="$string"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="delimiters" select="$delimiters"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="str:_tokenize-characters">
<xsl:param name="string"/>
<xsl:if test="$string">
<token>
<xsl:value-of select="substring($string, 1, 1)"/>
</token>
<xsl:call-template name="str:_tokenize-characters">
<xsl:with-param name="string" select="substring($string, 2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="str:_tokenize-delimiters">
<xsl:param name="string"/>
<xsl:param name="delimiters"/>
<xsl:variable name="delimiter" select="substring($delimiters, 1, 1)"/>
<xsl:choose>
<xsl:when test="not($delimiter)">
<token>
<xsl:value-of select="$string"/>
</token>
</xsl:when>
<xsl:when test="contains($string, $delimiter)">
<xsl:if test="not(starts-with($string, $delimiter))">
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="substring-before($string, $delimiter)"/>
<xsl:with-param name="delimiters" select="substring($delimiters, 2)"/>
</xsl:call-template>
</xsl:if>
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="substring-after($string, $delimiter)"/>
<xsl:with-param name="delimiters" select="$delimiters"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="delimiters" select="substring($delimiters, 2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
让我建议以下方法:
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"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="parts" select="document('parts.xml')"/>
<xsl:variable name="x-paths">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="normalize-space($parts//Fig[@Directory='OutputFiles'])"/>
<xsl:with-param name="delimiter" select="' '"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="x-steps">
<xsl:for-each select="exsl:node-set($x-paths)/token">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="x-ids">
<xsl:for-each select="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Component">
<xsl:variable name="steps">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="wix:File/@Source"/>
<xsl:with-param name="delimiter" select="'\'"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="exsl:node-set($steps)/token = exsl:node-set($x-steps)/token">
<id>
<xsl:value-of select="@Id"/>
</id>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="wix:Component | wix:ComponentRef">
<xsl:if test="not(@Id = exsl:node-set($x-ids)/id)">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'/'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<token>
<xsl:value-of select="$token"/>
</token>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
应用于您的示例输入,结果将是:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLDIRECTORY">
<Component Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" Guid="*">
<File Id="filF5C36F42ADA8B3DD927354B5AB666898" KeyPath="yes" Source="$(env.srcDir)\thisWillStay.txt"/>
</Component>
<Directory Id="dir2A411B40F7C80649B57155F53DD7D136" Name="ThisWillStay">
<Component Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" Guid="*">
<File Id="fil908CF12B8DD2E95A77D7611874EC3892" KeyPath="yes" Source="$(env.srcDir)\ThisWillStay\file1.txt"/>
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="DeliveredFiles">
<ComponentRef Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43"/>
<ComponentRef Id="cmpE7DF6F3C9BE17355EA10D49649C4957A"/>
</ComponentGroup>
</Fragment>
</Wix>
(抱歉,这可能有点长)
我有一个由 WiX 的热实用程序生成的 WXS 文件。我正在尝试使用(现有的)exclusions.xslt 文件对其进行修改,以根据另一个 XML 文件的内容自动排除某些组件(我将调用这个parts.xml)。 xslt文件目前用于从安装程序中删除一些components/files/directories,但对于一个相对静态的列表。
我正在转换的 WXS 文件有 File 元素,所有元素都有一个 "Source" 属性,这是删除元素所应用的标准。
我的目标是从我的 XSLT 样式表中读取 parts.xml 文件,并使用它从我的 Wix 安装程序中排除一些元素。我们目前排除元素的方式是:
我们首先将每个元素(整个源xml)复制过来。像这样:
<!-- Copy all attributes and elements to the output. -->
<xsl:template match="@*|*">
<xsl:copy>
<xsl:apply-templates select="@*" />
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
然后我们使用xsl:key指令来标记某些项目,像这样:
<xsl:key name="removals" match="wix:Component[wix:File[contains(@Source, ')\fileToBeRemoved1.txt')]]" use="@Id" />
<xsl:key name="removals" match="wix:Component[wix:File[contains(@Source, ')\fileToBeRemoved2.exe')]]" use="@Id" />
然后我们删除它们:
<xsl:template match="wix:Component[key('removals', @Id)]" />
<xsl:template match="wix:Directory[key('removals', @Id)]" />
<xsl:template match="wix:ComponentRef[key('removals', @Id)]" />
我想我已经到了能够将 parts.xml 文件读入样式表的地步:
<!-- Adapted from and http://geekswithblogs.net/Erik/archive/2008/04/01/120915.aspx-->
<xsl:param name="srcroot" />
<xsl:variable name="filename">
<xsl:call-template name="string-replace-all">
<xsl:with-param name="text" select="concat($srcroot, 'foo/parts.xml')" />
<xsl:with-param name="replace" select="'\'" />
<xsl:with-param name="by" select="'/'" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="partsfile" select="document(concat('file://', $filename))" />
<xsl:variable name="myOutputFiles">
<xsl:call-template name="str:tokenize">
<xsl:with-param name="string" select="normalize-space($partsfile/xsi:Apple/xsi:Banana[@Name = 'my_sdk']/xsi:Carrot/xsi:Fig[@Directory = 'OutputFiles'])"/>
</xsl:call-template>
</xsl:variable>
我从 here 得到了 str:tokenize。我想我设置 myOutputFiles 的方式存在问题,尤其是命名空间问题,但我不完全确定如何正确执行此操作。我已经将 xsi 命名空间添加到我的顶级样式表节点,但是当我打印出值时这似乎没有产生任何结果:
<xsl:message terminate="yes">
<xsl:text>INFO: </xsl:text>
<xsl:text>
</xsl:text>
<xsl:copy-of select="$myOutputFiles"/>
<xsl:text>
</xsl:text>
</xsl:message>
我的下一步是以某种方式对返回的每个标记调用
最终,一旦我得到标记,我想遍历它们并通过以类似于上面的方式标记它们来排除它们:
<xsl:key name="removals" match="wix:Component[wix:File[contains(@Source, ')\{PUT STRING FROM TOKEN HERE}')]]" use="@Id" />
然后它们将被上面的模板说明删除。 ')\' 前缀对于添加到令牌字符串很重要。 这就是它知道只从 INSTALLDIRECTORY 节点而不是任何子目录中删除组件的方式。
我怎样才能完成这个任务?非常感谢任何帮助!
编辑:
我使用 MSXSL 作为我的处理器。我不相信它本身可以做 str:tokenize,但我从 here 复制了模板并将其包含在我的样式表底部以供使用。除了消除
这是转换前的一些示例输入 XML:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLDIRECTORY">
<Component Id="cmp53284C0E6C6EC93D8D9DE8E3703596E4" Guid="*">
<File Id="filBD973F0EAED6A0B34BE693B053368769" KeyPath="yes" Source="$(env.srcDir)\file1.txt" />
</Component>
<Component Id="cmp81302C0E6C6EC93D877778E270358FFE" Guid="*">
<File Id="filAA273F0EAED6A0B34BE693B053A129EA" KeyPath="yes" Source="$(env.srcDir)\file2.exe" />
</Component>
<Component Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" Guid="*">
<File Id="filF5C36F42ADA8B3DD927354B5AB666898" KeyPath="yes" Source="$(env.srcDir)\thisWillStay.txt" />
</Component>
<Component Id="cmpABC123AE6C6EC93D8D9DE8E370BEEF39" Guid="*">
<File Id="fil72EA34F0EAED6A0B34BE693B05334421" KeyPath="yes" Source="$(env.srcDir)\Some.File.dll" />
</Component>
<Directory Id="dir2A411B40F7C80649B57155F53DD7D136" Name="ThisWillStay">
<Component Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" Guid="*">
<File Id="fil908CF12B8DD2E95A77D7611874EC3892" KeyPath="yes" Source="$(env.srcDir)\ThisWillStay\file1.txt" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="DeliveredFiles">
<ComponentRef Id="cmp53284C0E6C6EC93D8D9DE8E3703596E4" />
<ComponentRef Id="cmp81302C0E6C6EC93D877778E270358FFE" />
<ComponentRef Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" />
<ComponentRef Id="cmpABC123AE6C6EC93D8D9DE8E370BEEF39" />
<ComponentRef Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" />
</ComponentGroup>
</Fragment>
</Wix>
引用的 xml 文件 (parts.xml) 看起来像(通往 Fig 的节点树很重要,但它周围还有其他元素):
<?xml version="1.0" encoding="utf-8"?>
<Apple xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../foo/schema.xsd" >
<!-- ... -->
<Banana Name="my_sdk" SomeAttribute="${srcroot}some/path/to/file.txt">
<Carrot>
<Dill SomeOtherAttribute="SomeIdentifier"/>
<Fig Directory="OutputFiles">
foo/bar/file1.txt
foo/bar/file2.exe
foo/bar/Some.File.dll
<!-- ... -->
</Fig>
</Carrot>
</Banana>
<!-- ... -->
</Apple>
图中指定的文件与要从 WXS 文件中删除 的组件相关(尽管它们在此处的路径并不重要)。此参考文件基于 XML Schema 实例(schema.xsd 文件),类似于:
<?xml version="1.0" encoding="utf-8"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Apple">
<!-- (various xs:elements defined for elements used in other parts file) -->
</xs:element>
</xs:schema>
和预期输出XML:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLDIRECTORY">
<Component Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" Guid="*">
<File Id="filF5C36F42ADA8B3DD927354B5AB666898" KeyPath="yes" Source="$(env.srcDir)\thisWillStay.txt" />
</Component>
<Directory Id="dir2A411B40F7C80649B57155F53DD7D136" Name="ThisWillStay">
<Component Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" Guid="*">
<File Id="fil908CF12B8DD2E95A77D7611874EC3892" KeyPath="yes" Source="$(env.srcDir)\ThisWillStay\file1.txt" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="DeliveredFiles">
<ComponentRef Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" />
<ComponentRef Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" />
</ComponentGroup>
</Fragment>
</Wix>
file1.txt 组件现在从第一个片段中消失了,相应的 ComponentRef 在第二个片段中也消失了。 (路径对于参考文件无关紧要。它应该只使用文件名来排除 INSTALLDIRECTORY 节点中的文件,而不是源文件的任何子目录。我想我可以只使用 "substring-after-last" like this).
编辑2:
如果有帮助,这里有一些我从各种来源获取的模板,我打算使用它们:
<!--
Adapted from:
-->
<xsl:template name="substring-after-last">
<xsl:param name="haystack" />
<xsl:param name="needle" />
<xsl:choose>
<xsl:when test="contains($haystack, $needle)">
<xsl:call-template name="substring-after-last">
<xsl:with-param name="haystack"
select="substring-after($haystack, $needle)" />
<xsl:with-param name="needle" select="$needle" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$haystack" /></xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Tokenizing: http://exslt.org/str/functions/tokenize/index.html -->
<!-- Code from: http://exslt.org/str/functions/tokenize/str.tokenize.template.xsl -->
<xsl:template name="str:tokenize">
<xsl:param name="string" select="''"/>
<xsl:param name="delimiters" select="' '"/>
<xsl:choose>
<xsl:when test="not($string)"/>
<xsl:when test="not($delimiters)">
<xsl:call-template name="str:_tokenize-characters">
<xsl:with-param name="string" select="$string"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="delimiters" select="$delimiters"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="str:_tokenize-characters">
<xsl:param name="string"/>
<xsl:if test="$string">
<token>
<xsl:value-of select="substring($string, 1, 1)"/>
</token>
<xsl:call-template name="str:_tokenize-characters">
<xsl:with-param name="string" select="substring($string, 2)"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="str:_tokenize-delimiters">
<xsl:param name="string"/>
<xsl:param name="delimiters"/>
<xsl:variable name="delimiter" select="substring($delimiters, 1, 1)"/>
<xsl:choose>
<xsl:when test="not($delimiter)">
<token>
<xsl:value-of select="$string"/>
</token>
</xsl:when>
<xsl:when test="contains($string, $delimiter)">
<xsl:if test="not(starts-with($string, $delimiter))">
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="substring-before($string, $delimiter)"/>
<xsl:with-param name="delimiters" select="substring($delimiters, 2)"/>
</xsl:call-template>
</xsl:if>
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="substring-after($string, $delimiter)"/>
<xsl:with-param name="delimiters" select="$delimiters"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="str:_tokenize-delimiters">
<xsl:with-param name="string" select="$string"/>
<xsl:with-param name="delimiters" select="substring($delimiters, 2)"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
让我建议以下方法:
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"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:param name="parts" select="document('parts.xml')"/>
<xsl:variable name="x-paths">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="normalize-space($parts//Fig[@Directory='OutputFiles'])"/>
<xsl:with-param name="delimiter" select="' '"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="x-steps">
<xsl:for-each select="exsl:node-set($x-paths)/token">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
</xsl:call-template>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="x-ids">
<xsl:for-each select="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Component">
<xsl:variable name="steps">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="wix:File/@Source"/>
<xsl:with-param name="delimiter" select="'\'"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="exsl:node-set($steps)/token = exsl:node-set($x-steps)/token">
<id>
<xsl:value-of select="@Id"/>
</id>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- identity transform -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="wix:Component | wix:ComponentRef">
<xsl:if test="not(@Id = exsl:node-set($x-ids)/id)">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:if>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="'/'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<token>
<xsl:value-of select="$token"/>
</token>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
应用于您的示例输入,结果将是:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="INSTALLDIRECTORY">
<Component Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43" Guid="*">
<File Id="filF5C36F42ADA8B3DD927354B5AB666898" KeyPath="yes" Source="$(env.srcDir)\thisWillStay.txt"/>
</Component>
<Directory Id="dir2A411B40F7C80649B57155F53DD7D136" Name="ThisWillStay">
<Component Id="cmpE7DF6F3C9BE17355EA10D49649C4957A" Guid="*">
<File Id="fil908CF12B8DD2E95A77D7611874EC3892" KeyPath="yes" Source="$(env.srcDir)\ThisWillStay\file1.txt"/>
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<ComponentGroup Id="DeliveredFiles">
<ComponentRef Id="cmp2A1630B8E0E70C310FC91CD5DADB5A43"/>
<ComponentRef Id="cmpE7DF6F3C9BE17355EA10D49649C4957A"/>
</ComponentGroup>
</Fragment>
</Wix>