Wix Heat - Replace an autogenerated GUID with known string using XSLT

南楼画角 提交于 2019-12-06 15:22:44

You've got a number of problems with your XSLT, I am afraid. The first one is to do with namespaces. In your wix XML file, all the elements are within the namespace "http://schemas.microsoft.com/wix/2006/wi", but there is no mention of that namespace in the XSLT, meaning where you try to match a named element, it will only match ones which are in NO namespace, which is not the case for your wix file.

What you need to add a corresponding namespace declaration to you XSLT, like so

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

Then, where you reference named elements, prefix them with this declared prefix. For example, your template matching File would look like this

<xsl:template match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component/wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]">

Now, you've also got a problem within this template, because the first thing you do is output an attribute, but you've not actually copied the File element at this point, so you will probably get an error as it won't have anything to add the attributes to. The template, therefore, probably needs to look like this

<xsl:template match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component/wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]">
   <xsl:copy>
      <xsl:attribute name="Id">Windows_Service_exe</xsl:attribute>
      <xsl:copy-of select="@*[name()!='Id']"/>
      <xsl:apply-templates />
   </xsl:copy>
</xsl:template>

(I've added in an xsl:apply-templates here, but it is probably unnecessary in this case, if File elements don't ever have child elements)

You've also got problems with the previous template too

<xsl:template match="node[@Id=vIdToReplace]">

I am guessing you meant to use "node()" and not "node" here, as "node" itself would literally look for an element called "node" of which there are none. But the main problem is where you compare @Id with vIdToReplace. In this case, it is looking for an element called vIdToReplace, when you really want to compare it with your variable. The correct syntax is $vIdToReplace

<xsl:template match="node()[@Id=$vIdToReplace]">

But wait! If you are using XSLT 1.0, you will get an error. You cannot use variables in template matching like this in XSLT 1.0. It only works in XSLT 2.0. What you could do though, is simply paste in your long expression into the template match:

<xsl:template match="node()[@Id=//wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component[wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]]/@Id]">

But this looks rather ungainly. Alternatively, you could define a key to look up the Component element containing the id you wish to replace:

<xsl:key name="vIdToReplace"
         match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component[wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]]"
         use="@Id"/>

Then you can use this key in the template match, to check if the Component elements exists for the current @Id

<xsl:template match="node()[key('vIdToReplace', @Id)]">

Here is the full XSLT in this case

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wi="http://schemas.microsoft.com/wix/2006/wi">
  <xsl:key name="vIdToReplace"
  match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component[wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]]"
  use="@Id"/>

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

  <xsl:template match="node()[key('vIdToReplace', @Id)]">
    <xsl:copy>
      <xsl:attribute name="Id">C_Windows_Service_exe</xsl:attribute>
      <xsl:copy-of select="@*[name()!='Id']"/>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component/wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]">
     <xsl:copy>
        <xsl:attribute name="Id">Windows_Service_exe</xsl:attribute>
        <xsl:copy-of select="@*[name()!='Id']"/>
        <xsl:apply-templates />
     </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

When applied to your XML, the following is output

  <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  [...]
    <Fragment>
      <ComponentGroup Id="Windows.Service.Binaries">
        <ComponentRef Id="C_Windows_Service_exe"/>
        [...]
      </ComponentGroup>
    </Fragment>
    [...]
    <Fragment>
      <ComponentGroup Id="CG.WinSvcContent">
        <Component Id="C_Windows_Service_exe" Directory="TARGETDIR" Guid="{SOMEGUID}">
          <File Id="Windows_Service_exe" Source="$(var.Windows.Service.TargetDir)\Windows.Service.exe"/>
        </Component>
        [...]
      </ComponentGroup>
    </Fragment>
    [...]
  </Wix>
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!