Rename tag and remove attribute - xslt-1.0

I have an XML document (generated by WiX heat) where I want to remove the root element name while removing the attribute. The source tree looks like this
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<DirectoryRef Id="HELP" />
</Fragment>
</Wix>
I figured out how to rename the node, but this doesn't remove the unnecessary attribute xmlns.
<xsl:template match='/wix:Wix'>
<Include>
<xsl:copy-of select="#*|node()"/>
</Include>
</xsl:template>
<!-- Even this template doesn't suppress the attribute xmlns -->
<xsl:template match='#xmlns'/>
I event removed the #*| from the select clause. But this doesn't have any effect.
How can I produce with XSLT 1.0 the following desired output?
<Include>
<Fragment>
<DirectoryRef Id="HELP" />
</Fragment>
</Include>

this doesn't remove the unnecessary attribute xmlns.
xmlnsis not an attribute - it's a namespace, a part of the node's name. If you don't want it in the output, you cannot copy the input nodes that are in a namespace - you must create new nodes instead, for example:
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:template match="/wix:Wix">
<Include>
<xsl:apply-templates/>
</Include>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{local-name()}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Related

XSLT 1.0 - How to only copy elements with values, or descendents with values

With XML like
<?xml version="1.0" encoding="utf-8"?>
<Products>
<Product>
<Sku />
<Suppliers>
<Supplier>
<Name />
</Supplier>
</Suppliers>
<Priority>1</Priority>
</Product>
<Product>
<Sku>123</Sku>
<Suppliers>
<Supplier>Jon</Supplier>
</Suppliers>
<Priority>3</Priority>
<e />
</Product>
</Products>
How to transform and only output if element has value, or descendent has a value?
(The 2nd 'filter template' filters empty, with match=*[not(node() )] , but only if no descendents)
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- When matching empty: do nothing -->
<xsl:template match="*[not(node())]">
<xsl:comment>filtering <xsl:value-of select="local-name()"/></xsl:comment>
</xsl:template>
Current output: (With debug-comments to identify names of filtered elements)
<?xml version="1.0" encoding="utf-8"?><Products>
<Product>
<!--filtering Sku-->
<Suppliers>
<Supplier>
<!--filtering Name-->
</Supplier>
</Suppliers>
<Priority>1</Priority>
</Product>
<Product>
<Sku>123</Sku>
<Suppliers>
<Supplier>Jon</Supplier>
</Suppliers>
<Priority>3</Priority>
<!--filtering e-->
</Product>
</Products>
Required output: (without comments)
<?xml version="1.0" encoding="utf-8"?><Products>
<Product>
<Priority>1</Priority>
</Product>
<Product>
<Sku>123</Sku>
<Suppliers>
<Supplier>Jon</Supplier>
</Suppliers>
<Priority>3</Priority>
</Product>
</Products>
Assume this requires a type of forward-look (recursive template?), to see if any descendents have a value, before copying element
Assuming that by "value" you mean a non whitespace-only text node, you could do simply:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- prune branches that do not carry fruit -->
<xsl:template match="*[not(descendant::text())]"/>
</xsl:stylesheet>

WiX exclude certain file or certain file types under specific folder?

I have used "HeatDirectory" to harvest all the files. But I need to remove all the (*.xml and *.pdb) files under the "bin" folder.
I am able to remove individual files with any extension. But, How to remove the certain file or certain file types under the particular folder?
Below is the XSLT i am using to remove file based on the extension.
<?xml version="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">
<xsl:template match="#*|*">
<xsl:copy>
<xsl:apply-templates select="#*" />
<xsl:apply-templates select="*" />
</xsl:copy>
</xsl:template>
<xsl:output method="xml" indent="yes" />
<xsl:key name="RemoveGlobal.asax.cs"
match="wix:Component[contains(wix:File/#Source, 'Global.asax.cs')]" use="#Id"
/>
<xsl:key name="RemoveCsproj" match="wix:Component[contains(wix:File/#Source,
'.csproj')]" use="#Id" />
<xsl:template match="*[self::wix:Component or self::wix:ComponentRef]
[key('RemoveGlobal.asax.cs', #Id)]" />
<xsl:template match="*[self::wix:Component or self::wix:ComponentRef]
[key('RemoveCsproj', #Id)]" />
</xsl:stylesheet>

xslt - create empty file using xslt 1.0

I am trying to create an empty file through xslt.
The input sample is:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Businessman>
<siblings>
<sibling>John </sibling>
</siblings>
<child> Pete </child>
<child> Ken </child>
</Businessman>
When the input contains any presence of 'child' tags, it should produce the file AS IS. When the input does not have any 'child' tag, I need an empty file (0 byte file) created.
This is what I tried:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template match="#*|node()">
<xsl:choose>
<xsl:when test="/Businessman/child">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
This gives the file unchanged when there is any 'child' tag present. But did not produce any empty file when there is no 'child' tag.
The file I need to test will look like:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Businessman>
<siblings>
<sibling>John </sibling>
</siblings>
</Businessman>
Any help would be great!
Thanks
If you want the processor to go to the trouble of opening the output file, you have to give it something to write to the output file. Try an empty text node. And you only need to make the decision 'copy or not?' once.
One way to make the decision just once and produce empty output if the condition is not met would be to replace your template with:
<xsl:template match="/">
<xsl:choose>
<xsl:when test="/Businessman/child">
<xsl:copy-of select="*"/>
</xsl:when>
<xsl:otherwise>
<xsl:text/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
This works as expected with xsltproc. (If you find yourself getting a file containing an XML declaration and nothing else, try adjusting the parameters on xsl:output.)
But when I have found myself with a similar situation (perform this transform if condition C holds, otherwise ...), I have simply added a template for the document node that would look something like this for your case:
<xsl:choose>
<xsl:when test="/Businessman/child">
<xsl:apply-templates/>
</
<xsl:otherwise>
<xsl:message terminate="yes">No children in this input, dying ...</
</
</
That way I get no output at all rather than zero-length output.
Simple enough - Just don't try to do everything in one template, don't forget to omit the xml declaration and get the xpath right:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes" />
<xsl:template match="/">
<xsl:apply-templates />
</xsl:template>
<xsl:template match="Businessman[child]" priority="9">
<xsl:element name="Businessman">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="Businessman" priority="0" />
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Wix Toolset - apply Directory Conditions to its files

Consider this Directory,
<Directory Id="MyProgramDir" Name="DirName">
<Component Id="comp_MyProgramDir" Guid="FC0409CE-27E6-475E-B6C2-95E4B4C0223C" KeyPath="yes">
<Condition><![CDATA[MyCondition]]></Condition>
</Component>
</Directory>
I have to apply MyCondition to all children components of the Directory. Since it is a very big Directory containing many files, is there a way to avoid to write the condition for every component? The Transitive attribute is not helpful at all!
You can try this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
<xsl:output method="xml" indent="yes" cdata-section-elements="wix:Condition"/>
<xsl:strip-space elements="*" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="wix:Component">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:element name="Condition" namespace="http://schemas.microsoft.com/wix/2006/wi">
<xsl:attribute name="level">1</xsl:attribute>
<xsl:text>MyCondition</xsl:text>
</xsl:element>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
How to add condition to component during xsl transformation?

WiX 3.6 filtering with XSLT

I'm filtering all the .pdb files from my project. On the internet I saw several examples of how to do this with XSLT (because I'm not a XSLT-master I copied some and tried a few).
When I have the following XSLT script:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="service-search" match="wix:Component[contains(wix:File/#Source, '.pdb')]" use="#Id" />
<xsl:template match="wix:ComponentRef[key('service-search', #Id)]" />
</xsl:stylesheet>
But when I execute this with the pre-build event command:
call "C:\Program Files (x86)\WiX Toolset v3.6\bin\heat.exe" dir "..\bin" -t Filter.xslt -sfrag -cg "WebBinaries" -gg -srd -var "var.$(ProjectName).TargetDir" -dr "WebBin" -out "$(SolutionDir)\Deployment\$(ProjectName).binaries.wxs"
I get the following error: Found orphaned Component ......
The reference to the pdb files are correctly removed but a reference to the component that is removed is still there
When I change
<xsl:template match="wix:ComponentRef[key('service-search', #Id)]" />
to
<xsl:template match="wix:ComponentRef[key('service-search', #Id)]" />
I get another error: Unresolved reference to symbol ........ in section fragment
Does anybody know how to solve this problem?
Thanks in advance
I fixed it with the following xslt:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi">
<xsl:output method="xml" indent="yes" />
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:key name="service-search" match="wix:Component[contains(wix:File/#Source, '.pdb')]" use="#Id" />
<xsl:template match="wix:Component[key('service-search', #Id)]" />
<xsl:template match="wix:ComponentRef[key('service-search', #Id)]" />
</xsl:stylesheet>