can you help me to get the expected output:
No Restriction to use XSLT 2.0
Conditions:
contains(C1,'00000') and contains(C2,'H') then Its Header
contains(C1,'00000') and contains(C2,'D') Then Its details and its multiple records between Header and Trailer
contains(C1,'00000') and contains(C2,'D') then Trailer
Input:
<Root-Element>
<test>
<C1>CCO 11220210132015000001</C1>
<C2>H</C2>
<C3/>
</test>
<test>
<C1>CCO 11220210132015000002</C1>
<C2>D</C2>
<C3>20220210132015594860990011000000053002000020220210081075140001000002 Y</C3>
</test>
<test>
<C1>CCO 1120210132015000003</C1>
<C2>D</C2>
<C3>20220210132015577823920019000000047002000020220210081075140002000003 Y</C3>
</test>
<test>
<C1>CCO 11220210132015000007</C1>
<C2>T</C2>
<C3>000000138000000002885156</C3>
</test>
<test>
<C1>CCO 11220210132015000001</C1>
<C2>H</C2>
<C3/>
</test>
<test>
<C1>CCO 11220210132015000002</C1>
<C2>D</C2>
<C3>20220210132015594860990011000000053002000020220210081075140001000002 Y</C3>
</test>
<test>
<C1>CCO 1120210132015000003</C1>
<C2>D</C2>
<C3>20220210132015577823920019000000047002000020220210081075140002000003 Y</C3>
</test>
<test>
<C1>CCO 1120210132015000004</C1>
<C2>D</C2>
<C3>20220210132015577823920019000000047002000020220210081075140002000003 Y</C3>
</test>
<test>
<C1>CCO 11220210132015000007</C1>
<C2>T</C2>
<C3>000000138000000002885155</C3>
</test>
</Root-Element>
XSLT :
<xsl:stylesheet version="2.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="/Root-Element">
<Details>
<xsl:apply-templates select="test" />
</Details>
</xsl:template>
<xsl:template match="test[contains(C1,'00000') and contains(C2,'H')]">
<Header>
<xsl:apply-templates/>
</Header>
</xsl:template>
<xsl:template match="test[contains(C1,'00000') and contains(C2,'D')]">
<Details>
<xsl:apply-templates/>
<!--xsl:apply-templates select="following-sibling::test[contains(C1,'000003') and contains(C2,'D')][1]/*"/-->
</Details>
</xsl:template>
<xsl:template match="test[contains(C1,'00000') and contains(C2,'T')]">
<Control>
<xsl:apply-templates/>
</Control>
</xsl:template>
<xsl:template match="test" />
</xsl:stylesheet>
Received Output
<?xml version="1.0" encoding="utf-16"?>
<Details>
<Header>CCO 11220210132015000001H</Header>
<Details>CCO 11220210132015000002D20220210132015594860990011000000053002000020220210081075140001000002 Y</Details>
<Details>CCO 1120210132015000003D20220210132015577823920019000000047002000020220210081075140002000003 Y</Details>
<Control>CCO 11220210132015000007T000000138000000002885156</Control>
<Header>CCO 11220210132015000001H</Header>
<Details>CCO 11220210132015000002D20220210132015594860990011000000053002000020220210081075140001000002 Y</Details>
<Details>CCO 1120210132015000003D20220210132015577823920019000000047002000020220210081075140002000003 Y</Details>
<Details>CCO 1120210132015000004D20220210132015577823920019000000047002000020220210081075140002000003 Y</Details>
<Control>CCO 11220210132015000007T000000138000000002885155</Control>
</Details>
Expected Output:
<?xml version="1.0" encoding="utf-16"?>
<Details>
<Batch>
<Header>CCO 11220210132015000001H</Header>
<Details>CCO 11220210132015000002D20220210132015594860990011000000053002000020220210081075140001000002 Y</Details>
<Details>CCO 1120210132015000003D20220210132015577823920019000000047002000020220210081075140002000003 Y</Details>
<Control>CCO 11220210132015000007T000000138000000002885156</Control>
</Batch>
<Batch>
<Header>CCO 11220210132015000001H</Header>
<Details>CCO 11220210132015000002D20220210132015594860990011000000053002000020220210081075140001000002 Y</Details>
<Details>CCO 1120210132015000003D20220210132015577823920019000000047002000020220210081075140002000003 Y</Details>
<Details>CCO 1120210132015000004D20220210132015577823920019000000047002000020220210081075140002000003 Y</Details>
<Control>CCO 11220210132015000007T000000138000000002885155</Control>
</Batch>
</Details>
If you're using XSLT 2.0+ then this is a case for <xsl:for-each-group selec="test" group-starting-with="test[C2='H']">
If you're still on XSLT 1.0 then I'm afraid it's not easy. Look up "sibling recursion" for ideas.
Please, in XSLT questions, always state if there's a constraint on which version you can use. Many problems like this become vastly easier with 2.0 or 3.0.
==UPDATE==
With XSLT 2.0+, this would be something like:
<xsl:template match="Root-Element">
<Details>
<xsl:for-each-group select="test"
group-starting-with="test[C2='H']">
<Batch>
<xsl:apply-templates select="current-group()"/>
</Batch>
</xsl:for-each-group>
</Details>
</xsl:template>
followed by your template rules for test elements as before.
Related
Output.xml is already formed.
I have to parse Input.xml to find Feature_ID for a Test_ID from the mapping in Input.xml and add it to Output.xml.
I was thinking this can be done with XSLT. How would the XSLT look like?
Input.xml
<Mapping>
<Test>
<Test_ID>123</Test_ID>
<Feature_ID>111</Feature_ID>
</Test>
<Test>
<Test_ID>456</Test_ID>
<Feature_ID>222</Feature_ID>
</Test>
</Mapping>
Current (already formed) Output.xml
<?xml version="1.0" encoding="UTF-8"?>
<Suite>
<Test>
<Test_ID>123</Test_ID>
<Test_Name>Test_First</Test_Name>
</Test>
<Test>
<Test_ID>456</Test_ID>
<Test_Name>Test_Second</Test_Name>
</Test>
</Suite>
Desired Output.xml
<Suite>
<Test>
<Test_ID>123</Test_ID>
<Test_Name>Test_First</Test_Name>
<Feature_ID>111</Feature_ID>
</Test>
<Test>
<Test_ID>456</Test_ID>
<Test_Name>Test_Second</Test_Name>
<Feature_ID>222</Feature_ID>
</Test>
</Suite>
Also, how to pass Output.xml in the below command?
xsltproc XSLT.xsl Input.xml > Output_New.xml
To copy elements from one document to another, consider document() function in an XSLT script. Then call xsltproc on only run the main input document.
Actually, depending on your desired result, Input should be Output and vice versa since the root is Suite.
XSLT (notice Input.xml referenced inside)
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/Suite">
<xsl:copy>
<xsl:apply-templates select="Test"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Test">
<xsl:copy>
<xsl:variable name="curr_id" select="Test_ID"/>
<xsl:variable name="expr" select="document('FeatureID_Mapping.xml')/Mapping/Test[Test_ID = $curr_id]"/>
<xsl:copy-of select="Test_ID|Test_Name"/>
<xsl:choose>
<xsl:when test="$expr">
<xsl:copy-of select="$expr/Feature_ID"/>
</xsl:when>
<xsl:otherwise>
<Feature_ID/>
</xsl:otherwise>
</xsl:choose>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
xsltproc
xsltproc myScript.xsl Output.xml > myDesiredResult.xml
Result
<Suite>
<Test>
<Test_ID>123</Test_ID>
<Test_Name>Test_First</Test_Name>
<Feature_ID>111</Feature_ID>
</Test>
<Test>
<Test_ID>456</Test_ID>
<Test_Name>Test_Second</Test_Name>
<Feature_ID>222</Feature_ID>
</Test>
</Suite>
How do I output just the last duplicate node WITHIN the upper/parent element using XSLT 1.0? I use xsltproc processor.
Input.xml
<testng-results>
<suite>
<test>
<class name="system.apps.CreateTerritory">
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="FAIL" name="ABC"> </test-method>
</class>
<class name="system.apps.CreateAccount">
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="SKIP" name="DEF"> </test-method>
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="FAIL" name="DEF"> </test-method>
</class>
<class name="system.apps.CreateProposal">
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="SKIP" name="DEF"> </test-method>
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="FAIL" name="DEF"> </test-method>
</class>
</test>
</suite>
</testng-results>
My Current XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" extension-element-prefixes="str" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:key name="test-by-name" match="test-method[not(#is-config)]" use="#name" />
<xsl:template match="/">
<Suite>
<xsl:for-each select="testng-results/suite/test/class">
<xsl:for-each select="test-method[not(#is-config)][count(. | key('test-by-name', #name)[last()]) = 1]">
<Test>
<Class_Name>
<xsl:value-of select="../#name"/>
</Class_Name>
<Method_Name>
<xsl:value-of select="#name"/>
</Method_Name>
<Status>
<xsl:value-of select="#status"/>
</Status>
</Test>
</xsl:for-each>
</xsl:for-each>
</Suite>
</xsl:template>
</xsl:stylesheet>
Note: I can't change the way the nested match is done (for-each class and then for-each test-method as I need to be this way for other reasons)
Current Output.xml (the problem is that the duplicity is being checked ACROSS classes instead of WITHIN that class):
<?xml version="1.0" encoding="UTF-8"?>
<Suite>
<Test>
<Class_Name>system.apps.CreateTerritory</Class_Name>
<Method_Name>ABC</Method_Name>
<Status>FAIL</Status>
</Test>
<Test>
<Class_Name>system.apps.CreateProposal</Class_Name>
<Method_Name>DEF</Method_Name>
<Status>FAIL</Status>
</Test>
</Suite>
Expected Output.xml (only the last node is outputted for every duplicate test-method WITHIN THAT CLASS):
<?xml version="1.0" encoding="UTF-8"?>
<Suite>
<Test>
<Class_Name>system.apps.CreateTerritory</Class_Name>
<Method_Name>ABC</Method_Name>
<Status>FAIL</Status>
</Test>
<Test>
<Class_Name>system.apps.CreateAccount</Class_Name>
<Method_Name>DEF</Method_Name>
<Status>FAIL</Status>
</Test>
<Test>
<Class_Name>system.apps.CreateProposal</Class_Name>
<Method_Name>DEF</Method_Name>
<Status>FAIL</Status>
</Test>
</Suite>
As I mentioned in a comment on your previous question, you need to add a class identifier to the key. Assuming that each class has a unique name, this could be:
<xsl:key name="test-by-name" match="test-method[not(#is-config)]" use="concat(#name, '|', ../#name)" />
Then:
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:key name="test-by-name" match="test-method[not(#is-config)]" use="concat(#name, '|', ../#name)" />
<xsl:template match="/testng-results">
<Suite>
<xsl:for-each select="suite/test/class/test-method[not(#is-config)][count(. | key('test-by-name', concat(#name, '|', ../#name))[last()]) = 1]">
<Test>
<Class_Name>
<xsl:value-of select="../#name"/>
</Class_Name>
<Method_Name>
<xsl:value-of select="#name"/>
</Method_Name>
<Status>
<xsl:value-of select="#status"/>
</Status>
</Test>
</xsl:for-each>
</Suite>
</xsl:template>
</xsl:stylesheet>
will return:
Result
<?xml version="1.0" encoding="UTF-8"?>
<Suite>
<Test>
<Class_Name>system.apps.CreateTerritory</Class_Name>
<Method_Name>ABC</Method_Name>
<Status>FAIL</Status>
</Test>
<Test>
<Class_Name>system.apps.CreateAccount</Class_Name>
<Method_Name>DEF</Method_Name>
<Status>FAIL</Status>
</Test>
<Test>
<Class_Name>system.apps.CreateProposal</Class_Name>
<Method_Name>DEF</Method_Name>
<Status>FAIL</Status>
</Test>
</Suite>
How do I output just the last duplicate node using XSLT 1.0?
I use xsltproc processor.
Input.xml
<testng-results>
<suite>
<test>
<class name="system.apps.CreateTerritory">
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="FAIL" name="ABC"> </test-method>
</class>
<class name="system.apps.CreateAccount">
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="SKIP" name="DEF"> </test-method>
<test-method status="PASS" name="initTest" is-config="true"> </test-method>
<test-method status="FAIL" name="DEF"> </test-method>
</class>
</test>
</suite>
</testng-results>
My Current XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:str="http://exslt.org/strings" extension-element-prefixes="str" version="1.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="/">
<Suite>
<xsl:for-each select="testng-results/suite/test/class">
<xsl:for-each select="test-method">
<xsl:if test="not(#is-config)">
<Test>
<Method_Name>
<xsl:value-of select="#name"/>
</Method_Name>
<Status>
<xsl:value-of select="#status"/>
</Status>
</Test>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</Suite>
</xsl:template>
</xsl:stylesheet>
Note: I can't change the way the nested match is done (for-each class and then for-each test-method as I need to be this way for other reasons)
Current Output.xml:
<?xml version="1.0" encoding="UTF-8"?>
<Suite>
<Test>
<Method_Name>ABC</Method_Name>
<Status>FAIL</Status>
</Test>
<Test>
<Method_Name>DEF</Method_Name>
<Status>SKIP</Status>
</Test>
<Test>
<Method_Name>DEF</Method_Name>
<Status>FAIL</Status>
</Test>
</Suite>
Expected Output.xml (only the last node is outputted for every duplicate test-method):
<?xml version="1.0" encoding="UTF-8"?>
<Suite>
<Test>
<Method_Name>ABC</Method_Name>
<Status>FAIL</Status>
</Test>
<Test>
<Method_Name>DEF</Method_Name>
<Status>FAIL</Status>
</Test>
</Suite>
If you're using xsltproc (that is the libxslt processor), you can take advantage of the EXSLT set:distinct() extension function in order to utilize a shortened version of Muenchian grouping:
XSLT 1.0 (+EXSLT)
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:set="http://exslt.org/sets"
extension-element-prefixes="set">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:key name="test-by-name" match="test-method[not(#is-config)]" use="#name" />
<xsl:template match="/testng-results">
<xsl:variable name="tests" select="suite/test/class/test-method[not(#is-config)]" />
<Suite>
<xsl:for-each select="set:distinct($tests/#name)">
<Test>
<Method_Name>
<xsl:value-of select="."/>
</Method_Name>
<Status>
<xsl:value-of select="key('test-by-name', .)[last()]/#status"/>
</Status>
</Test>
</xsl:for-each>
</Suite>
</xsl:template>
</xsl:stylesheet>
Or, if you prefer, you can do it in pure XSLT 1.0 as:
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:key name="test-by-name" match="test-method[not(#is-config)]" use="#name" />
<xsl:template match="/testng-results">
<Suite>
<xsl:for-each select="suite/test/class/test-method[not(#is-config)][count(. | key('test-by-name', #name)[last()]) = 1]">
<Test>
<Method_Name>
<xsl:value-of select="."/>
</Method_Name>
<Status>
<xsl:value-of select="#status"/>
</Status>
</Test>
</xsl:for-each>
</Suite>
</xsl:template>
</xsl:stylesheet>
Note the use of the [last()] predicate instead of the usual [1].
I am looking for a simple split of an xml file just based on tags; say 3 tags always repeat and need split as depicted below:
Input
<?xml version="1.0" encoding="UTF-8"?>
<Test>
<tag1>A</tag1>
<tag2>B</tag2>
<tag3>C</tag3>
<tag1>1</tag1>
<tag2>2</tag2>
<tag3>3</tag3>
<tag1>apple</tag1>
<tag2>orange</tag2>
<tag3>mango</tag3>
</Test>
Expected Output
<Root>
<Test>
<tag1>A</tag1>
<tag2>B</tag2>
<tag3>C</tag3>
</Test>
<Test>
<tag1>1</tag1>
<tag2>2</tag2>
<tag3>3</tag3>
</Test>
<Test>
<tag1>apple</tag1>
<tag2>orange</tag2>
<tag3>mango</tag3>
</Test>
</Root>
Any help is appreciated
Thanks
If the structure is regular, 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:template match="/Test">
<Root>
<xsl:for-each select="tag1">
<Test>
<xsl:copy-of select=". | following-sibling::tag2[1] | following-sibling::tag3[1] "/>
</Test>
</xsl:for-each>
</Root>
</xsl:template>
</xsl:stylesheet>
I'm using a testing tool that generates XML like this
<detail>
<results>
<component>A</component>
<test>
<property>performance</property>
<result>FAIL</result>
</test>
<test>
<property>memory</property>
<result>PASS</result>
</test>
</results>
<results>
<component>B</component>
<test>
<property>files</property>
<result>PASS</result>
</test>
<test>
<property>handles</property>
<result>FAIL</result>
</test>
</results>
</detail>
Note that each results element contains 1 component element and 0-* test elements. I want to get the text of each detail>results>component and detail>results>test>property where a detail>results>test>result text is 'FAIL'.
Is there an XPath expression that would do this?
If not, is there a simple XSLT to transform the example into something I can import into a spreadsheet like
A,performance,FAIL
A,memory,PASS
B,files,PASS
B,handles,FAIL
This Xpath will give you the properties that have failed:
//detail/results/test[result = 'FAIL']/property
And this will give you the names of the components that have a partial fail:
//detail/results[test/result = 'FAIL']/component
Combining these you can find all results that have a fail:
//detail/results[test/result = 'FAIL']
And then iterate inside this to get all failed tests in that result and returns the proprty that failed:
$result/test[result = 'FAIL']/property
I'm not on a machine where I can test an XSL yet, but putting one together from the above XPaths shouldn't be difficult.
When the below transformations
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:template match="detail">
<xsl:for-each select="results">
<xsl:for-each select="test">
<xsl:value-of select="preceding-sibling::component"/><xsl:text>,</xsl:text>
<xsl:value-of select="child::property"/><xsl:text>,</xsl:text>
<xsl:value-of select="child::result"/><xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
runs on below XML
<detail>
<results>
<component>A</component>
<test>
<property>performance</property>
<result>FAIL</result>
</test>
<test>
<property>memory</property>
<result>PASS</result>
</test>
</results>
<results>
<component>B</component>
<test>
<property>files</property>
<result>PASS</result>
</test>
<test>
<property>handles</property>
<result>FAIL</result>
</test>
</results>
</detail>
gives the required Output:
A,performance,FAIL
A,memory,PASS
B,files,PASS
B,handles,FAIL
I would recommend to prefer 'template match' and apply-template before for-each.
Therefor try this:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:strip-space elements="*" />
<xsl:output method="xml" indent="yes"/>
<xsl:template match="detail">
<xsl:apply-templates select="results[test/result='FAIL']/test" />
</xsl:template>
<xsl:template match="results/test">
<xsl:value-of select="../component"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="property"/>
<xsl:text>,</xsl:text>
<xsl:value-of select="result"/>
<xsl:text>
</xsl:text>
</xsl:template>
</xsl:stylesheet>