I have a requirement where I'd like to have if else statement to check whether a node has attributes or it has just string.
Eg: 1 of the node has 0 File(s) found
and the other has attribs such as <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='64' filename='AFP_p.tgp' />
Below is a sample of two nodes
<product> <autoIncludeUser>0 File(s) found</autoIncludeUser> <autoIncludeSystem> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='64' filename='AFP_p.tgp' /> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='3,879' filename='AnalystsExpressionMacros.tgp' /> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='475' filename='base64Converter.tgp' /> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='<DIR>' filename='codePages' /> </autoIncludeSystem> <autoIncludeStudio>0 File(s) found</autoIncludeStudio> <externalLibrarySystem> <externalLibrarySystem_info mdate='08/23/2011' mtime='09:52' ampm='PM' filesize='196,608' filename='AFPtoXML_DP.dll' /> <externalLibrarySystem_info mdate='08/23/2011' mtime='09:52' ampm='PM' filesize='13,259' filename='ASN1toXSDRunner.jar' /> <externalLibrarySystem> </product>
How would i identify if a node has just strings or attribs and based on that I can get the values either String
or attrib values
respectively.
You can replace your whole xsl:choose
instruction with:
<xsl:apply-templates select="autoIncludeSystem"/>
and then add two templates:
<xsl:template match="autoIncludeSystem[autoincludesystem_info/@*]> <!-- code for elements with attributes (xsl:when) --> </xsl:template> <xsl:template match="autoIncludeSystem[not(autoincludesystem_info/@*)]> <!-- code for elements without attributes (xsl:otherwise) --> </xsl:template>
We can achieve if else by using below code
<xsl:choose> <xsl:when test="something to test"> </xsl:when> <xsl:otherwise> </xsl:otherwise> </xsl:choose>
So here is what I did
<h3>System</h3> <xsl:choose> <xsl:when test="autoIncludeSystem/autoincludesystem_info/@mdate"> <!-- if attribute exists--> <p> <dd><table border="1"> <tbody> <tr> <th>File Name</th> <th>File Size</th> <th>Date</th> <th>Time</th> <th>AM/PM</th> </tr> <xsl:for-each select="autoIncludeSystem/autoincludesystem_info"> <tr> <td valign="top" ><xsl:value-of select="@filename"/></td> <td valign="top" ><xsl:value-of select="@filesize"/></td> <td valign="top" ><xsl:value-of select="@mdate"/></td> <td valign="top" ><xsl:value-of select="@mtime"/></td> <td valign="top" ><xsl:value-of select="@ampm"/></td> </tr> </xsl:for-each> </tbody> </table> </dd> </p> </xsl:when> <xsl:otherwise> <!-- if attribute does not exists --> <dd><pre> <xsl:value-of select="autoIncludeSystem"/><br/> </pre></dd> <br/> </xsl:otherwise> </xsl:choose>
My Output
I. Xpath 1.0 solution - use this single XPath expression:
concat(substring('String', 1 div boolean(text())), ' ', substring('attrib values', 1 div boolean(@*)) )
Here is a XSLT-based verification:
<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="*[not(*)]"> <xsl:value-of select="concat(' ', name(),': ')"/> <xsl:value-of select= "concat(substring('String', 1 div boolean(text())), ' ', substring('attrib values', 1 div boolean(@*)) ) "/> </xsl:template> </xsl:stylesheet>
when this transformation is applied on the provided XML document (slightly corrected to be made well-formed):
<product> <autoIncludeUser>0 File(s) found</autoIncludeUser> <autoIncludeSystem> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='64' filename='AFP_p.tgp' /> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='3,879' filename='AnalystsExpressionMacros.tgp' /> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='475' filename='base64Converter.tgp' /> <autoincludesystem_info mdate='08/23/2011' mtime='09:51' ampm='PM' filesize='<DIR>' filename='codePages' /> </autoIncludeSystem> <autoIncludeStudio>0 File(s) found</autoIncludeStudio> <externalLibrarySystem> <externalLibrarySystem_info mdate='08/23/2011' mtime='09:52' ampm='PM' filesize='196,608' filename='AFPtoXML_DP.dll' /> <externalLibrarySystem_info mdate='08/23/2011' mtime='09:52' ampm='PM' filesize='13,259' filename='ASN1toXSDRunner.jar' /> </externalLibrarySystem> </product>
the wanted, correct result is produced:
autoIncludeUser: String autoincludesystem_info: attrib values autoincludesystem_info: attrib values autoincludesystem_info: attrib values autoincludesystem_info: attrib values autoIncludeStudio: String externalLibrarySystem_info: attrib values externalLibrarySystem_info: attrib values
Explanation We use these facts:
For any string $s
, substring($s, Infinity)
is the empty string.
1 div 0
is Infinity
By definition number(true())
is 1
and number(false())
is 0.
II. XPath 2.0 solution:
This is much easier in XPath 2.0 because the language has standard if/then/else
operator.
Use:
if(text()) then 'String' else if(@*) then 'attrib values' else ()
XSLT 2.0 verification:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="text"/> <xsl:strip-space elements="*"/> <xsl:template match="*[not(*)]"> <xsl:value-of select="concat(' ', name(),': ')"/> <xsl:value-of select= "if(text()) then 'String' else if(@*) then 'attrib values' else () "/> </xsl:template> </xsl:stylesheet>
when this transformation is applied on the same XML document (above), again the wanted, correct result is produced:
autoIncludeUser: String autoincludesystem_info: attrib values autoincludesystem_info: attrib values autoincludesystem_info: attrib values autoincludesystem_info: attrib values autoIncludeStudio: String externalLibrarySystem_info: attrib values externalLibrarySystem_info: attrib values
XPath //*[not(@*)]
will select all elements, which don't have attributes.
You can do this easily enough using xsl:choose
- but very often in XSLT, the better way to do conditional processing is to write different template rules to handle the different conditions. So write one template rule with match="*[@*]"
to match elements that have attributes, and another with match="*[text()]"
to match elements that have textual content.