Reputation: 616
Input XML:
<testng-results>
<suite>
<test>
<class>
<test-method status="PASS" description="Test_ID:123,Test_Name:Test ABC,Product:Product ABC"></test-method>
<test-method status="PASS" description="Test_ID:456,Test_Name:Test XYZ,Product:Product XYZ"></test-method>
</class>
</test>
</suite>
</testng-results>
My current XSLT:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<Suite>
<xsl:for-each select="testng-results/suite/test/class/test-method">
<test>
<xsl:attribute name="status">
<xsl:value-of select="@status" />
</xsl:attribute>
<xsl:attribute name="Test_ID">
<xsl:value-of select="" />
</xsl:attribute>
<xsl:attribute name="Test_Name">
<xsl:value-of select="" />
</xsl:attribute>
<xsl:attribute name="Product">
<xsl:value-of select="" />
</xsl:attribute>
</test>
</xsl:for-each>
</Suite>
EXPECTED OUTPUT.XML:
<?xml version="1.0" encoding="UTF-8"?>
<Suite>
<test status="PASS" Test_ID="123" Test_Name="Test ABC" Product="Product ABC" />
<test status="PASS" Test_ID="456" Test_Name="Test XYZ" Product="Product XYZ" />
</Suite>
I have to get the string from the 'description' value and split the values to generate the output xml.
I have read that XSLT 2.0 is better equipped with string tokenize functions. But, I am restricted to use XSLT 1.0.
Upvotes: 1
Views: 3190
Reputation: 116959
If your processor supports the EXSLT str:tokenize()
extension function, you can do:
XSLT 1.0 + EXSLT
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/testng-results">
<Suite>
<xsl:for-each select="suite/test/class/test-method">
<test status="{@status}" >
<xsl:for-each select="str:tokenize(@description, ',')">
<xsl:attribute name="{substring-before(., ':')}">
<xsl:value-of select="substring-after(., ':')" />
</xsl:attribute>
</xsl:for-each>
</test>
</xsl:for-each>
</Suite>
</xsl:template>
</xsl:stylesheet>
Otherwise you could just extract each of the 3 required values using string functions:
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="*"/>
<xsl:template match="/testng-results">
<Suite>
<xsl:for-each select="suite/test/class/test-method">
<test
status="{@status}"
Test_ID="{substring-before(substring-after(@description, 'Test_ID:'), ',') }"
Test_Name="{substring-before(substring-after(@description, 'Test_Name:'), ',') }"
Product="{substring-after(@description, 'Product:')}"/>
</xsl:for-each>
</Suite>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1
Reputation: 29022
You can achieve this with a named template retrieving the appropriate values by using xsl:call-template
.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<Suite>
<xsl:for-each select="testng-results/suite/test/class/test-method">
<test>
<xsl:attribute name="status">
<xsl:value-of select="@status" />
</xsl:attribute>
<xsl:attribute name="Test_ID">
<xsl:call-template name="subElem">
<xsl:with-param name="str" select="@description" />
<xsl:with-param name="itemName" select="'Test_ID'" />
</xsl:call-template>
</xsl:attribute>
<xsl:attribute name="Test_Name">
<xsl:call-template name="subElem">
<xsl:with-param name="str" select="@description" />
<xsl:with-param name="itemName" select="'Test_Name'" />
</xsl:call-template>
</xsl:attribute>
<xsl:attribute name="Product">
<xsl:call-template name="subElem">
<xsl:with-param name="str" select="@description" />
<xsl:with-param name="itemName" select="'Product'" />
</xsl:call-template>
</xsl:attribute>
</test>
</xsl:for-each>
</Suite>
</xsl:template>
<xsl:template name="subElem">
<xsl:param name="str" />
<xsl:param name="itemName" />
<xsl:variable name="res" select="substring-after(substring-after($str,$itemName),':')" />
<xsl:choose>
<xsl:when test="contains($res,',')">
<xsl:value-of select="substring-before($res,',')" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$res" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The individual items are retrieved by their name.
Its output is:
<?xml version="1.0"?>
<Suite>
<test status="PASS" Test_ID="123" Test_Name="Test ABC" Product="Product ABC"/>
<test status="PASS" Test_ID="456" Test_Name="Test XYZ" Product="Product XYZ"/>
</Suite>
Upvotes: 1
Reputation: 8783
I propose this approach. It is quite rough, but it is sure, because it relays on standard XSLT elements and functions only:
(To tokenize, it implements a recursive algorithm.)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes"/>
<xsl:template match="/">
<Suite>
<xsl:apply-templates select="testng-results/suite/test/class/test-method"></xsl:apply-templates>
</Suite>
</xsl:template>
<xsl:template match="test-method">
<test>
<xsl:call-template name="tokenize-to-attrs">
<xsl:with-param name="s" select="@description"/>
</xsl:call-template>
</test>
</xsl:template>
<xsl:template name="tokenize-to-attrs">
<xsl:param name="s"/>
<xsl:variable name="pair" select="substring-before($s, ',')"/>
<xsl:if test="$pair">
<xsl:call-template name="to-attr">
<xsl:with-param name="s" select="$pair"/>
</xsl:call-template>
<xsl:call-template name="tokenize-to-attrs">
<xsl:with-param name="s" select="substring-after($s, ',')"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="not($pair)">
<xsl:call-template name="to-attr">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="to-attr">
<xsl:param name="s"/>
<xsl:variable name="name" select="substring-before($s, ':')"/>
<xsl:variable name="value" select="substring-after($s, ':')"/>
<xsl:attribute name="{$name}">
<xsl:value-of select="$value"/>
</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
Upvotes: 1