Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xslt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用XSLT1.0根据引用的XML文件中的元素删除元素(WXS组件/文件/etc)_Xslt_Wix_Xslt 1.0_Msxsl - Fatal编程技术网

使用XSLT1.0根据引用的XML文件中的元素删除元素(WXS组件/文件/etc)

使用XSLT1.0根据引用的XML文件中的元素删除元素(WXS组件/文件/etc),xslt,wix,xslt-1.0,msxsl,Xslt,Wix,Xslt 1.0,Msxsl,(抱歉,这可能有点长) 我有一个WXS文件,它是由WiX的heat实用程序生成的。我正试图使用一个(现有的)exclusions.xslt文件对其进行修改,以便根据另一个XML文件的内容自动排除某些组件(我称之为parts.XML)。xslt文件目前用于从安装程序中删除一些组件/文件/目录,但用于相对静态的列表 我正在转换的WXS文件有file元素,所有这些元素都有一个“Source”属性,这是用于删除元素的条件 我的目标是从XSLT样式表中读入parts.xml文件,并使用它从Wix安装程序中

(抱歉,这可能有点长)

我有一个WXS文件,它是由WiX的heat实用程序生成的。我正试图使用一个(现有的)exclusions.xslt文件对其进行修改,以便根据另一个XML文件的内容自动排除某些组件(我称之为parts.XML)。xslt文件目前用于从安装程序中删除一些组件/文件/目录,但用于相对静态的列表

我正在转换的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 https://stackoverflow.com/a/30153713/5605122 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>

我从中得到str:tokenize。我想我设置myOutputFiles的方式会有问题,特别是名称空间问题,但我不完全确定如何正确地做到这一点。我已经将xsi名称空间添加到我的顶级样式表节点,但是当我使用以下命令打印出值时,这似乎没有产生任何结果:

<xsl:message terminate="yes">
    <xsl:text>INFO: </xsl:text>
    <xsl:text>&#xd;</xsl:text>
    <xsl:copy-of select="$myOutputFiles"/>
    <xsl:text>&#xd;</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,但我从中复制了模板,并将其包含在样式表的底部以供使用。除了去掉标签,它似乎还可以工作。如果我用string=“'file1file2file3'”调用它,并以与上面打印的相同方式打印出来,它会将“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的节点树很重要,但它周围还有其他元素):


)

编辑2:

如果有帮助的话,下面是我从各种来源获得的一些模板,我计划使用这些模板:

<!--
Adapted from: https://stackoverflow.com/a/9079154/5605122
-->
<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>

让我建议以下方法:

XSLT1.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>


1。请发布一个XML输入示例以及预期结果-请参阅:。--2.您确定处理器支持
str:tokenize()
函数吗?您希望能够将变量引用放入键的
匹配模式
?我认为XSLT1.0不允许这样做。对此感到抱歉。我用输入和预期输出以及一个示例参考xml文件更新了这个问题。@MartinHonnen,是的,我希望有一些方法可以基于这个变量添加到“removals”键中。有没有其他方法可以不使用说明就完成同样的任务?谢谢@AndrewDiNunzio坦白说,我听不懂你的解释。为什么输出中包含
Source=“$(env.srcDir)\thisWillStay.txt”
,但
Source=“$(env.srcDir)\file1.txt”
不包含?在引用的xml文件(带有标记的文件)中,有一个带有
<!--
Adapted from: https://stackoverflow.com/a/9079154/5605122
-->
<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>
<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>