Reputation: 455
I've transformed an XML to plain text using xsl, I want to output the text of the nodes and attributes of it. I want the xsl to be generic, without containing the name of the node. I managed to do this. But when I have an empty node in xml it will be written in the output. I don't want that. How can I stop writting an empty node or attribute using xsl? I am looking for an implementation where it does not check for the name of a node but for all the nodes.
I have the following XML.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<run xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<tests>
<test name="a" attr1="" attr2="" attr3="an_value">
<usecase name="name1">
<description>Some description</description>
</usecase>
<usecase name="name2">
<description>Descripton1</description>
</usecase>
<usecase name="name3">
<description>Descripton2</description>
</usecase>
</test>
</tests>
<vip>
<file name="b">
<stat wins="1"/>
<justifications/>
</file>
<file name="c">
<stat wins="2" />
<justifications/>
</file>
</vip>
</run>
I have the following XSL.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:value-of select="local-name()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="text()" />
<xsl:call-template name="attributes"/>
<xsl:text>
</xsl:text>
<xsl:apply-templates select=" node()"/>
</xsl:template>
<xsl:template name="attributes">
<xsl:for-each select="@*">
<xsl:value-of select="local-name()"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Output:
run
tests
test name : a attr1 : attr2 : attr3 : an_value
usecase name : name1
description Some description
usecase name : name2
description Descripton1
usecase name : name3
description Descripton2
vip
file name : b
stat wins : 1
justifications
file name : c
stat wins : 2
justifications
what xsl command I can use to not display the node justifications and the attributes attr1 and attr2 if they are all empty without hardcoding the xsl?
Desired output
run
tests
test name : a attr3 : an_value
usecase name : name1
description Some description
usecase name : name2
description Descripton1
usecase name : name3
description Descripton2
vip
file name : b
stat wins : 1
file name : c
stat wins : 2
EDIT: I updated the xsl to this
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no"/>
<xsl:variable name="empty_string" select="''" />
<xsl:strip-space elements="*"/>
<xsl:template match="node()">
<xsl:value-of select="local-name()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="text()" />
<xsl:call-template name="attributes"/>
<xsl:text>
</xsl:text>
<xsl:apply-templates select=" node()"/>
</xsl:template>
<xsl:template name="attributes">
<xsl:for-each select="@*">
<xsl:if test="normalize-space(.) != $empty_string">
<xsl:value-of select="local-name()"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
I am still looking on a condition to not write a node if it has no attributes and no childs. In that wasy the justification node will not be displayed.
Upvotes: 1
Views: 645
Reputation: 116993
The example is somewhat ambiguous, and the logic that needs to be applied here is not entirely clear. I am guessing you want to do something like:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="*[@*|node()]">
<xsl:value-of select="name()"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="@*[string()]|text()"/>
<xsl:text> </xsl:text>
<xsl:apply-templates select="*"/>
</xsl:template>
<xsl:template match="@*">
<xsl:value-of select="name()"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:template>
</xsl:stylesheet>
Personally, I wouldn't use space as a delimiter, because it can easily appear as part of a value (and so can a line feed, actually).
Upvotes: 1
Reputation: 2714
Here's a way you could do it:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output method="text"/>
<xsl:template match="node()">
<xsl:if test="text() or node() or @*">
<xsl:text>
</xsl:text>
<xsl:value-of select="local-name()"/>
<xsl:text> </xsl:text>
<xsl:value-of select="normalize-space(text())"/>
</xsl:if>
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
<xsl:template match="@*">
<xsl:if test=".!=''">
<xsl:value-of select="local-name()"/>
<xsl:text> : </xsl:text>
<xsl:value-of select="."/>
<xsl:text> </xsl:text>
</xsl:if>
<xsl:apply-templates select="@*|node()"/>
</xsl:template>
</xsl:stylesheet>
See it working here: https://xsltfiddle.liberty-development.net/bEzknt1/1
Upvotes: 0