Search code examples
xmlxsltwixheat

Removing unwanted nodes from wxs file by applying xslt tranfsormation


I am trying to cleanup wxs file generated by heat.exe by applying xml transformations.

Below is an example file output of heat.exe.

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
            <DirectoryRef Id="APPFOLDER">
                <Component Id="cmp78E9FF58917B1844F3E9315A285F3147" Guid="SOME-GUID">
                    <File Id="fil093D6D7CB723B5B62730D7B4E575F154" KeyPath="yes" Source="PQR.Some.dll" />
                </Component>
                <Component Id="cmp0B084126FAE7577FD84DB29766AC6C2B" Guid="SOME-GUID">
                    <File Id="filB20C8708D7EB02EDFBCC4D70F9FE7F8A" KeyPath="yes" Source="ABC.Another.dll" />
                </Component>
                <Component Id="cmp83BB1954DECD7D949AAE4ACA68806EC3" Guid="SOME-GUID">
                    <File Id="fil0E29FBFF7DB39F307A2EE19237A0A579" KeyPath="yes" Source="ABC.OneMore.dll" />
                </Component>
            </DirectoryRef>
        </Fragment>
        <Fragment>
            <ComponentGroup Id="AppFiles">
                <ComponentRef Id="cmp78E9FF58917B1844F3E9315A285F3147" />
                <ComponentRef Id="cmp0B084126FAE7577FD84DB29766AC6C2B" />
                <ComponentRef Id="cmp83BB1954DECD7D949AAE4ACA68806EC3" />
            </ComponentGroup>
        </Fragment>
    </Wix>

I want to remove Component nodes whose child File node having a Source attribute containing the string 'ABC'. I know how to find such nodes using the correct matching patterns. So before I remove the Component node, I want to store the Id of the component and then use it to remove the ComponentRef node with the Id which i just recorded.

Is there a way I can acheive this with XML transformation? I think I am looking for something where I can create a variable say 'X' to store the Id of Component node that I delete, and use 'X' to find the ComponentRef nodes to be deleted.


Solution

  • You can do this without variables. Like this:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:wi="http://schemas.microsoft.com/wix/2006/wi">
      <xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
      <xsl:strip-space elements="*"/>
      <xsl:key name="kCompsToRemove"
               match="wi:Component[contains(wi:File/@Source, 'ABC')]"
               use="@Id" />
    
      <xsl:template match="@* | node()">
        <xsl:copy>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
      </xsl:template>
    
      <xsl:template match="*[self::wi:Component or self::wi:ComponentRef]
                            [key('kCompsToRemove', @Id)]" />
    </xsl:stylesheet>
    

    When run on your sample input, this produces:

    <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
      <Fragment>
        <DirectoryRef Id="APPFOLDER">
          <Component Id="cmp78E9FF58917B1844F3E9315A285F3147" Guid="SOME-GUID">
            <File Id="fil093D6D7CB723B5B62730D7B4E575F154" KeyPath="yes" Source="PQR.Some.dll" />
          </Component>
        </DirectoryRef>
      </Fragment>
      <Fragment>
        <ComponentGroup Id="AppFiles">
          <ComponentRef Id="cmp78E9FF58917B1844F3E9315A285F3147" />
        </ComponentGroup>
      </Fragment>
    </Wix>