XSLT code to pass a value in output XML based on a condition in input XML - xslt

Input XML:
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<RECORDS>
<Group>
<Name>12345</Name>
<Grp>MANAGER</Grp>
<FName>Alex</FName>
<LName>Johnson</LName>
<String1>abcd</String1>
/Group>
<Group>
<Name>67891</Name>
<Grp>PROJECT MANAGER</Grp>
<FName>JAMES</FName>
<LName>HARPER</LName>
<String1></String1>
</Group> </RECORDS> <LOGIN>
<User>
<Name>12345</UserName>
<Last>14/02/2013</Last>
</User>
<User>
<Name>67891</Name>
<Last>14/01/2013/Last>
</User> </LOGIN> </DATA>
Requirement:
In output XML
If String1 has a value then Type tag should have value as "axbx" and
if String1 is blank then Type tag should have value as "dydy"
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<RECORDS>
<Group>
<Name>12345</Name>
<Grp>MANAGER</Grp>
<FName>Alex</FName>
<LName>Johnson</LName>
<Type>axbx</Type>
</Group>
<Group>
<Name>67891</Name>
<Grp>PROJECT MANAGER</Grp>
<FName>JAMES</FName>
<LName>HARPER</LName>
<Type>dydy</Type>
</Group> </RECORDS> </DATA>
Please suggest.

I can't edit your question so I copy the corrected XML:
<?xml version="1.0" encoding="UTF-8"?>
<DATA>
<RECORDS>
<Group>
<Name>12345</Name>
<Grp>MANAGER</Grp>
<FName>Alex</FName>
<LName>Johnson</LName>
<String1>abcd</String1>
</Group>
<Group>
<Name>67891</Name>
<Grp>PROJECT MANAGER</Grp>
<FName>JAMES</FName>
<LName>HARPER</LName>
<String1></String1>
</Group>
</RECORDS>
<LOGIN>
<User>
<Name>12345</Name>
<Last>14/02/2013</Last>
</User>
<User>
<Name>67891</Name>
<Last>14/01/2013</Last>
</User>
</LOGIN>
</DATA>
and the XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="String1">
<Type>
<xsl:choose>
<xsl:when test="string-length(.) > 0">axbx</xsl:when>
<xsl:otherwise>dydy</xsl:otherwise>
</xsl:choose>
</Type>
</xsl:template>
</xsl:stylesheet>
I'm not very experienced so there might be a better way.

Related

Flatten XML after copy

I have a requirement to create a copy of the xml record based on a repeating field which I am able to do so, however I need the result to be flattened
I have tried to use variables and copying them to the output
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Hire>
<xsl:for-each select="Hire/Record">
<xsl:variable name="var_record" select="./*
[not(name()='Sweldo')]" />
<xsl:for-each select="Sweldo">
<xsl:variable name="var_SWELDO" select=".">
</xsl:variable>
<Record>
<xsl:copy-of select="$var_record" />
<xsl:copy-of select="$var_SWELDO" />
</Record>
</xsl:for-each>
</xsl:for-each>
</Hire>
</xsl:template>
</xsl:stylesheet>
The input is
<?xml version="1.0" encoding="UTF-8"?>
<Hire>
<Record>
<XRefCode>XX</XRefCode>
<EmployeeNumber>161</EmployeeNumber>
<BirthDate>1985-04-09</BirthDate>
<SocialSecurityNumber>XXXXXXX</SocialSecurityNumber>
<FirstName>XX</FirstName>
<LastName>XX</LastName>
<MiddleName>D</MiddleName>
<Sweldo>
<sahod>ONE MILLION</sahod>
</Sweldo>
<Sweldo>
<sahod>1 BILLION</sahod>
</Sweldo>
</Record>
</Hire>
The output I am getting is
<?xml version="1.0"?>
<Hire>
<Record>
<XRefCode>161</XRefCode>
<EmployeeNumber>161</EmployeeNumber>
<BirthDate>1985-04-09</BirthDate>
<SocialSecurityNumber>999-81-9462</SocialSecurityNumber>
<FirstName>XXX</FirstName>
<LastName>XXXXX</LastName>
<MiddleName>D</MiddleName>
<Sweldo>
<sahod>ONE MILLION</sahod>
</Sweldo>
</Record>
<Record>
<XRefCode>161</XRefCode>
<EmployeeNumber>161</EmployeeNumber>
<BirthDate>1985-04-09</BirthDate>
<SocialSecurityNumber>999-81-9462</SocialSecurityNumber>
<FirstName>XXX</FirstName>
<LastName>XXXX</LastName>
<MiddleName>D</MiddleName>
<Sweldo>
<sahod>1 BILLION</sahod>
</Sweldo>
</Record>
</Hire>
However I need the following format
<?xml version="1.0"?>
<Hire>
<Record>
<XRefCode>161</XRefCode>
<EmployeeNumber>161</EmployeeNumber>
<BirthDate>1985-04-09</BirthDate>
<SocialSecurityNumber>999-81-9462</SocialSecurityNumber>
<FirstName>XXXX</FirstName>
<LastName>XXXXXX</LastName>
<MiddleName>D</MiddleName>
<sahod>ONE MILLION</sahod>
</Record>
<Record>
<XRefCode>161</XRefCode>
<EmployeeNumber>161</EmployeeNumber>
<BirthDate>1985-04-09</BirthDate>
<SocialSecurityNumber>999-81-9462</SocialSecurityNumber>
<FirstName>XXXX</FirstName>
<LastName>XXXX</LastName>
<MiddleName>D</MiddleName>
<sahod>1 BILLION</sahod>
</Record>
</Hire>
Is there a way to completely remove the element?
Please check and update following code:-
<xsl:for-each select="Sweldo">
**change to**
<xsl:for-each select="Sweldo/sahod">

Verticalize XML using XSLT

I am trying to implement an at first looking simple transformation but whatever I have tried has been failed.
The XML is generated from a fixed length record and have the below format.
<?xml version="1.0" encoding="UTF-8"?>
<record>
<no_of_records>30</no_of_records>
<cust_lastname_1>Smith</cust_lastname_1>
<cust_name_1>John</cust_name_1>
<cust_id_1>X45</cust_id_1>
<cust_lastname_2>George</cust_lastname_2>
<cust_name_2>Michael</cust_name_2>
<cust_id_2>X76</cust_id_2>
<cust_lastname_3>Ria</cust_lastname_3>
<cust_name_3>Chris</cust_name_3>
<cust_id_3>C87</cust_id_3>
...
</record>
The no_of_records indicates how many _X suffixed elements contains each record and because of its fix length origin has a defined maximum.
I want to transform it to a “verticalized” form resempling the below.
<record>
<customer num="1">
<lastname>Smith</lastname>
<name>John</name>
<id>X45</id>
</customer>
<customer num="2">
<lastname>George</lastname>
<name>Michael</name>
<id>X76</id>
</customer>
<customer num="3">
<lastname>Ria</lastname>
<name>Chris</name>
<id>C87</id>
...
</customer>
</record>
Any help would greatly appreciated.
In XSLT 2.0, you want something like
<xsl:for-each-group select="*" group-starting-with="*[starts-with(local-name(), 'cust_lastname']">
<customer num="{position()}">
<xsl:apply-templates select="current-group()"/>
</customer>
</xsl:for-each-group>
....
<xsl:template match="*[starts-with(local-name(), 'cust')]">
<xsl:element name="{replace(local-name(), 'cust_(.*?)_[0-9]+', '$1')}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
The solution from #Michael Kay works fine. Thank you !
XML
<?xml version="1.0" encoding="UTF-8"?>
<record>
<no_of_records>3</no_of_records>
<cust_lastname_1>Smith</cust_lastname_1>
<cust_name_1>John</cust_name_1>
<cust_id_1>X45</cust_id_1>
<cust_lastname_2>George</cust_lastname_2>
<cust_name_2>Michael</cust_name_2>
<cust_id_2>X76</cust_id_2>
<cust_lastname_3>Ria</cust_lastname_3>
<cust_name_3>Chris</cust_name_3>
<cust_id_3>C87</cust_id_3>
</record>
XSLT
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="record">
<records>
<xsl:for-each-group select="*[starts-with(local-name(), 'cust_')]"
group-starting-with="*[starts-with(local-name(), 'cust_lastname')]">
<customer num="{position()}">
<xsl:apply-templates select="current-group()"/>
</customer>
</xsl:for-each-group>
</records>
</xsl:template>
<xsl:template match="*[starts-with(local-name(), 'cust')]">
<xsl:element name="{replace(local-name(), 'cust_(.*?)_[0-9]+', '$1')}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<records>
<customer num="1">
<lastname>Smith</lastname>
<name>John</name>
<id>X45</id>
</customer>
<customer num="2">
<lastname>George</lastname>
<name>Michael</name>
<id>X76</id>
</customer>
<customer num="3">
<lastname>Ria</lastname>
<name>Chris</name>
<id>C87</id>
</customer>
</records>

Not able to group similar records in XSLT

I am trying to group all similar records based on language. But I am not able to group in XSLT.
I am using XSL KEY function group the record in XSLT. I am trying loop and add each group records to one group.
I have the following input xml.
<root>
<element name="David" language="German"></element>
<element name="Sarah" language="German"></element>
<element name="Isaac" language="English"></element>
<element name="Abraham" language="German"></element>
<element name="Jackson" language="English"></element>
<element name="Deweher" language="English"></element>
<element name="Jonathan" language="Hindi"></element>
<element name="Mike" language="Hindi"></element>
</root>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="xs"
version="1.0">
<xsl:key name="lang" match="element" use="#language"></xsl:key>
<xsl:template match="/">
<root>
<xsl:for-each select="key('lang',//element/#language)">
<Group>
<xsl:attribute name="name" select=".//#language"></xsl:attribute>
<member><xsl:value-of select=".//#name"/></member>
</Group>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
Expected Output :
<root>
<Group name="German">
<member>David</member>
<member>Sarah</member>
<member>Abraham</member>
</Group>
<Group name="English">
<member>Isaac</member>
<member>Jackson</member>
<member>Deweher</member>
</Group>
<Group name="Hindi">
<member>Jonathan</member>
<member>Mike</member>
</Group>
</root>
Actual Output :
<root>
<Group name="German">
<member>David</member>
</Group>
<Group name="German">
<member>Sarah</member>
</Group>
<Group name="English">
<member>Isaac</member>
</Group>
<Group name="German">
<member>Abraham</member>
</Group>
<Group name="English">
<member>Jackson</member>
</Group>
<Group name="English">
<member>Deweher</member>
</Group>
<Group name="Hindi">
<member>Jonathan</member>
</Group>
<Group name="Hindi">
<member>Mike</member>
</Group>
</root>
I am getting each records separately.
Can someone please let me know what went wrong in the XSL. Thanks :)
I made some changes in your stylesheet. This should achieve the result you expect:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="lang" match="element" use="#language"></xsl:key>
<xsl:template match="root">
<xsl:copy>
<xsl:for-each select="element[count(. | key('lang', #language)[1]) = 1]">
<Group name="{#language}">
<xsl:for-each select="key('lang', #language)">
<member><xsl:value-of select="#name"/></member>
</xsl:for-each>
</Group>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The first loop selects each unique language (a node-set of size 3), and creates a context for the inner loop. The inner loop iterates through each element and selects only the ones that have the same language.
Muenchian grouping may seem hard to grasp, but you can always apply the template shown in this tutorial and not have to think much. I simply applied that template to your example.
UPDATE: Here is a solution without using for-each loops:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output indent="yes"/>
<xsl:key name="lang" match="element" use="#language"></xsl:key>
<xsl:template match="root">
<xsl:copy>
<xsl:apply-templates select="element[generate-id(.) = generate-id(key('lang', #language)[1])]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="element">
<Group name="{#language}">
<xsl:apply-templates select="key('lang', #language)" mode="member"/>
</Group>
</xsl:template>
<xsl:template match="element" mode="member">
<member><xsl:value-of select="#name"/></member>
</xsl:template>
</xsl:stylesheet>

How to merge two xml file using xslt1.0?

File1.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<data>
<title>Title1</title>
<description>Description1</description>
<myid>1</myid>
</data>
<data>
<title>Title2</title>
<description>Description2</description>
<myid>2</myid>
</data>
</catalog>
File2.xml:
<?xml version="1.0" encoding="ISO-8859-1"?>
<catalog>
<data>
<author>Author1</author>
<date>12/34/5678</date>
<myid>1</myid>
</data>
<data>
<author>Author2</author>
<date>87/65/4321</date>
<myid>2</myid>
</data>
</catalog>
need output like below using xslt1.0
<catalog>
<data>
<title>Title1</title>
<description>Description1</description>
<myid>1</myid>
<author>Author1</author>
<date>12/34/5678</date>
</data>
<data>
<title>Title2</title>
<description>Description2</description>
<myid>2</myid>
<author>Author2</author>
<date>87/65/4321</date>
</data>
</catalog>
You need to use the document() function, like so:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
<xsl:template match="/">
<catalog>
<!-- Apply to all data elements in file 1 -->
<xsl:apply-templates select="document('file1.xml')/catalog/data" />
</catalog>
</xsl:template>
<xsl:template match="data">
<data>
<!--Use myid as a lookup-->
<xsl:variable name="myId" select="myid/text()" />
<!--copy all data child nodes from file1-->
<xsl:copy-of select="#* | node()"/>
<!--copy all data child nodes from file2, excluding myid as we already have it-->
<xsl:copy-of select="document('file2.xml')/catalog/data[myid=$myId]/*[not(local-name()='myid')]"/>
</data>
</xsl:template>
</xsl:stylesheet>

Getting attribute names and doing some manipulations in xslt

I have to get the attribute names and do some manipulations based on the its name in XSLT.
Source:
<group xlink:type="simple" xlink:href="XXX" xlink:title="sectionHeader_1" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="YYY" xlink:title="BodyParagraph_1" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="ZZZ" xlink:title="sectionHeader_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="AAA" xlink:title="sectionHeader_3" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="BBB" xlink:title="BodyParagraph_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="BBB" xlink:title="ConditionalText_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
I have to get the attribute xlink:title attribute in it and check for the following:
when the attribute xlink:title contains string sectionHeader , I need to do some manipulations.
when the attribute xlink:title contains string BodyParagraph, I need to some manipulations.
when the attribute xlink:title contains string ConditionalText, I need to some manipulations.
Can any one explain how it can be done?
It is in the spirit of XSLT to use templates and pattern matching so that explicit conditional instructions are minimized or eliminated altogether.
Here is how this can be done:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xlink="http://www.w3.org/1999/xlink">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="group">
<xsl:apply-templates select="#xlink:title"/>
</xsl:template>
<xsl:template match="#xlink:title[contains(., 'sectionHeader')]">
Found #xlink:title containing "sectionHeader"
</xsl:template>
<xsl:template match="#xlink:title[contains(., 'BodyParagraph')]">
Found #xlink:title containing "BodyParagraph"
</xsl:template>
<xsl:template match="#xlink:title[contains(., 'ConditionalText')]">
Found #xlink:title containing "ConditionalText"
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML fragment (converted to a well-formed XML document):
<t>
<group xlink:type="simple" xlink:href="XXX" xlink:title="sectionHeader_1" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="YYY" xlink:title="BodyParagraph_1" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="ZZZ" xlink:title="sectionHeader_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="AAA" xlink:title="sectionHeader_3" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="BBB" xlink:title="BodyParagraph_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group xlink:type="simple" xlink:href="BBB" xlink:title="ConditionalText_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
</t>
the wanted result (something done in each case) is produced:
Found #xlink:title containing "sectionHeader"
Found #xlink:title containing "BodyParagraph"
Found #xlink:title containing "sectionHeader"
Found #xlink:title containing "sectionHeader"
Found #xlink:title containing "BodyParagraph"
Found #xlink:title containing "ConditionalText"
Do note: You may consider using the starts-with() function rather than contains() .
Since you can't modify parts of an existing XML file with XSLT, you have to copy everything and change those parts that should be different. Thus, I suggest to write a template that copies each node by default. Then you can add specialized templates for the group elements that meet your conditions, e.g. something like this:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xlink="http://www.w3.org/1999/xlink">
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="group[contains(#xlink:title,'sectionHeader')]">
<group>
<xsl:copy-of select="#*"/>
<!-- modifications here -->
</group>
</xsl:template>
<xsl:template match="group[contains(#xlink:title,'BodyParagraph')]">
<group>
<xsl:copy-of select="#*"/>
<!-- modifications here -->
</group>
</xsl:template>
<xsl:template match="group[contains(#xlink:title,'ConditionalText')]">
<group>
<xsl:copy-of select="#*"/>
<!-- modifications here -->
</group>
</xsl:template>
</xsl:stylesheet>
If you want to change the attribute values too, just replace the xsl:copy-of statements with the desired modifications.
Your source XML (some modifications done):
<?xml version="1.0"?>
<root>
<group id="x1" xlink:type="simple" xlink:href="XXX" xlink:title="sectionHeader_1" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group id="x2" xlink:type="simple" xlink:href="YYY" xlink:title="BodyParagraph_1" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group id="x3" xlink:type="simple" xlink:href="ZZZ" xlink:title="sectionHeader_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group id="x4" xlink:type="simple" xlink:href="AAA" xlink:title="sectionHeader_3" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group id="x5" xlink:type="simple" xlink:href="BBB" xlink:title="BodyParagraph_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group id="x6" xlink:type="simple" xlink:href="BBB" xlink:title="ConditionalText_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
<group id="x7" xlink:type="simple" xlink:href="BBB" xlink:title="some_other_2" xmlns:xlink="http://www.w3.org/1999/xlink"></group>
</root>
XSL Document:
<?xml version='1.0'?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink">
<xsl:template match="/">
<elements>
<xsl:apply-templates/>
</elements>
</xsl:template>
<xsl:template match="//group[(contains(#xlink:title,'sectionHeader') or contains(#xlink:title,'BodyParagraph') or contains(#xlink:title,'ConditionalText'))]">
<xsl:element name="element">
<xsl:attribute name="id"><xsl:value-of select="#id"/></xsl:attribute>
<xsl:attribute name="type"><xsl:value-of select="#xlink:type"/></xsl:attribute>
<xsl:attribute name="href"><xsl:value-of select="#xlink:href"/></xsl:attribute>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
And the result:
<?xml version='1.0' ?>
<elements xmlns:xlink="http://www.w3.org/1999/xlink">
<element id="x1" type="simple" href="XXX"/>
<element id="x2" type="simple" href="YYY"/>
<element id="x3" type="simple" href="ZZZ"/>
<element id="x4" type="simple" href="AAA"/>
<element id="x5" type="simple" href="BBB"/>
<element id="x6" type="simple" href="BBB"/>
</elements>