XSLT - Generate unique id fora each distinct value - xslt-1.0

I'm trying to generate a unique id for each distinct value using XSLT-1.0. I have a XSMLstructure and every time when I find the same value in tag I would like to generate a unique id into the output tag .
Input XML:
<EMPLOYEES>
<EMPLOYEE>
<ID>1</ID>
<NAME>AAA</NAME>
</EMPLOYEE>
<EMPLOYEE>
<ID>2</ID>
<NAME>AAA</NAME>
</EMPLOYEE>
<EMPLOYEE>
<ID>3</ID>
<NAME>BBB</NAME>
</EMPLOYEE>
</EMPLOYEES>
Desired output:
<RESULT>
<EMPLOYEE>
<ID>1</ID>
<GROUP>1</GROUP>
</EMPLOYEE>
<EMPLOYEE>
<ID>2</ID>
<GROUP>1</GROUP>
</EMPLOYEE>
<EMPLOYEE>
<ID>3</ID>
<GROUP>2</GROUP>
</EMPLOYEE>
</RESULT>

You could use a variation of Muenchian grouping:
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:key name="emp-by-name" match="EMPLOYEE" use="NAME" />
<xsl:template match="/EMPLOYEES">
<RESULT>
<xsl:for-each select="EMPLOYEE">
<xsl:copy>
<xsl:copy-of select="ID"/>
<GROUP>
<xsl:value-of select="generate-id(key('emp-by-name', NAME)[1])"/>
</GROUP>
</xsl:copy>
</xsl:for-each>
</RESULT>
</xsl:template>
</xsl:stylesheet>
The format of the unique id depends on the processor you use: for example, using libxslt you might see a result like:
<?xml version="1.0" encoding="UTF-8"?>
<RESULT>
<EMPLOYEE>
<ID>1</ID>
<GROUP>idm86101615952</GROUP>
</EMPLOYEE>
<EMPLOYEE>
<ID>2</ID>
<GROUP>idm86101615952</GROUP>
</EMPLOYEE>
<EMPLOYEE>
<ID>3</ID>
<GROUP>idm86101613584</GROUP>
</EMPLOYEE>
</RESULT>
while MSXML can produce:
<?xml version="1.0"?>
<RESULT>
<EMPLOYEE>
<ID>1</ID>
<GROUP>IDOECYH2JCFIENET332KVV31ACSEILRTQLIHKWOCNC1WN5QJBD3AH</GROUP>
</EMPLOYEE>
<EMPLOYEE>
<ID>2</ID>
<GROUP>IDOECYH2JCFIENET332KVV31ACSEILRTQLIHKWOCNC1WN5QJBD3AH</GROUP>
</EMPLOYEE>
<EMPLOYEE>
<ID>3</ID>
<GROUP>IDZZBORVA3LQGFPDQLO51JC1X1JDCTYLF430BB43GE1YQ1D54SHZSB</GROUP>
</EMPLOYEE>
</RESULT>

Related

xslt 3.0 xsl:evaluate example

For the following xml document:
<company>
<employee>
<name>John Andrews</name>
<age>23</age>
<salary>4000</salary>
<division>Accounting</division>
</employee>
</company>
I have the xsl like
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:value-of select="company/employee/name"/>
<xsl:variable name="test">
<xsl:text>company/employee/name</xsl:text>
</xsl:variable>
<xsl:evaluate xpath="$test"/>
</xsl:template>
</xsl:stylesheet>
How can I use $test variable in xsl:evaluate in order to get the same result as in the:
<xsl:value-of select="company/employee/name"/>
Is it possible?
You need to set the context item with e.g.
<xsl:evaluate xpath="$test" context-item="."/>

Filter keys out of response

I have an xslt sheet in which I have 2 response objects. $response1 contains a list of ids something like:
<response>
<idlist>
<id>1</id>
<id>2</id>
</idlist>
</response>
And $response2 contains a number of objects:
<response2>
<obj id="1" name="obj1"/>
<obj id="2" name="obj2"/>
<obj id="3" name="obj3"/>
<obj id="4" name="obj4"/>
</response2>
I want to make a copy of response2 but filtering out any objects where the id matches thos contained in response 1
<xsl:variable name="copy">
<xsl:copy-of select="$response2/*[not contains($response1, id)]"/>
</xsl:variable>
any ideas greatly appreciated
C
Given a well-formed input such as:
<root>
<response>
<idlist>
<id>1</id>
<id>2</id>
</idlist>
</response>
<response2>
<obj id="1" name="obj1"/>
<obj id="2" name="obj2"/>
<obj id="3" name="obj3"/>
<obj id="4" name="obj4"/>
</response2>
</root>
the following stylesheet:
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="/root">
<xsl:variable name="ids" select="response/idlist/id" />
<output>
<xsl:copy-of select="response2/obj[not(#id=$ids)]"/>
</output>
</xsl:template>
</xsl:stylesheet>
will return:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<obj id="3" name="obj3"/>
<obj id="4" name="obj4"/>
</output>
A better solution is to use a key to link the nodes by matching id:
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:key name="id" match="id" use="." />
<xsl:template match="/root">
<output>
<xsl:copy-of select="response2/obj[not(key('id', #id))]"/>
</output>
</xsl:template>
</xsl:stylesheet>

How to map one XML with different XML through Data mapper in mule

I have an XML as below:
<?xml version='1.0' encoding='UTF-8'?>
<ns2:trigger xmlns:ns2="http://www.someurl.com/" id="1001">
<changes>
<change id="5" userName="12334">
<UserField name="Organisation">Organisation 1</UserField>
<UserField name="Age"/>
<UserField name="First name">1</UserField>
</change>
<change id="1" userName="abc">
<UserField name="Organisation">Organisation 1</UserField>
<UserField name="Age"/>
<UserField name="First name">Name</UserField>
</change>
<change employeeId="123" id="2" userName="hr">
<UserField id="8" name="Organisation">Firma AS</UserField>
<UserField id="33" name="Age"/>
<UserField id="2" name="Last name">HR</UserField>
</change>
<change employeeId="q123" id="16" userName="Ashish">
<UserField name="Organisation">Organisation 1</UserField>
<UserField name="Age"/>
<UserField name="Last name">Ashish Singh</UserField>
</change>
</changes>
</ns2:trigger>
I want to generate another XML from above XML which will look like as below:
<DATA>
<SAVE>
<EMPLOYEE>
<FIRST_NAME>1</FIRST_NAME>
<AGE></AGE>
<ORGANIZATION>Organisation 1</ORGANIZATION>
</EMPLOYEE>
<EMPLOYEE>
<FIRST_NAME>Name</FIRST_NAME>
<AGE></AGE>
<ORGANIZATION>Organisation 1</ORGANIZATION>
</EMPLOYEE>
<EMPLOYEE>
<LAST_NAME>HR</LAST_NAME>
<AGE></AGE>
<ORGANIZATION>Organisation 1</ORGANIZATION>
</EMPLOYEE>
<EMPLOYEE>
<LAST_NAME>Ashish Singh</LAST_NAME>
<AGE></AGE>
<ORGANIZATION>Organisation 1</ORGANIZATION>
</EMPLOYEE>
</SAVE>
</DATA>
I have researched on data mapper and tried to get required XML but I am not able to achieve it. I am doing this mapping and it is giving me following output:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<save>
<employee>
<name>Organisation</name>
</employee>
<employee>
<name>Age</name>
</employee>
<employee>
<name>First name</name>
</employee>
<employee>
<name>Organisation</name>
</employee>
<employee>
<name>Age</name>
</employee>
<employee>
<name>First name</name>
</employee>
<employee>
<name>Organisation</name>
</employee>
<employee>
<name>Age</name>
</employee>
<employee>
<name>Last name</name>
</employee>
<employee>
<name>Organisation</name>
</employee>
<employee>
<name>Age</name>
</employee>
<employee>
<name>Last name</name>
</employee>
</save>
</data>
Please tell me how to do it through data mapper.
Your target mapping structure looks incorrect to me. You will need to amend or re-create this.
I'd suggest you re-create the metadata based on the sample XML you've provided above. In your DataMapper window, above the output mapping box, hit the little wand button and select Re-Create Metadata. Then you want to generate it from a sample file and use the XML you've provided.

XSLT: Incremental variable and parameter assigning

Hi
i need the below xml
<Data>
<Employees>
<Employee>
<EmployeeName>Ram</EmployeeName>
<EmployeeID>123</EmployeeID>
<Gender>M</Gender>
</Employee>
<Employee>
<EmployeeName>Helen</EmployeeName>
<EmployeeID>432</EmployeeID>
<Gender>F</Gender>
</Employee>
<Employee>
<EmployeeName>Dinesh</EmployeeName>
<EmployeeID>321</EmployeeID>
<Gender>M</Gender>
</Employee>
</Employees>
</Data>
converting to this
<?xml version="1.0" encoding="UTF-8"?>
<Employees>
<Employee Gender="Male" Current="true" index="1">
<Name>Ram</Name>
</Employee>
<Employee Gender="Male" Current="false" index="2">
<Name>Dinesh</Name>
</Employee>
<Employee Gender="Female" Current="false" index="3">
<Name>Helen</Name>
</Employee>
</Employees>
The stylesheet i have used is as this
<?xml version="1.0" encoding="UTF-8"?>
<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="Data">
<Employees>
<xsl:for-each select="Employees/Employee[Gender = 'M']">
<Employee Gender="Male">
<Name>
<xsl:value-of select="EmployeeName"/>
</Name>
</Employee>
</xsl:for-each>
<xsl:for-each select="Employees/Employee[Gender = 'F']">
<Employee Gender="Female">
<Name>
<xsl:value-of select="EmployeeName"/>
</Name>
</Employee>
</xsl:for-each>
</Employees>
</xsl:template>
</xsl:stylesheet>
I tried using several sample such as but nothings seems to work out. Can any one help me out in this please? This is just a sample code i have put together to explain the problem.
Or to be more specific,
"Current" is required to be set in the first employee node only. The index on the other hand should be on all the nodes.
You can use position() to determine the element's position under its parent. I would also tend to refactor the template in favour of apply-templates and to DRY up the repeated Employee mapping for male and female.
<?xml version="1.0" encoding="UTF-8"?>
<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="Data">
<Employees>
<xsl:apply-templates select="Employees/Employee">
<xsl:sort select="Gender" order="descending" />
</xsl:apply-templates>
</Employees>
</xsl:template>
<xsl:template match="Employee">
<xsl:variable name="gender">
<xsl:choose>
<xsl:when test="Gender='M'">Male</xsl:when>
<xsl:when test="Gender='F'">Female</xsl:when>
<xsl:otherwise>Unknown</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="current">
<xsl:choose>
<xsl:when test="position() = 1">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<Employee Gender="{$gender}" Current="{$current}" index="{position()}">
<Name>
<xsl:value-of select="EmployeeName"/>
</Name>
</Employee>
</xsl:template>
</xsl:stylesheet>
Edit As per #Ians solution, I've added the sorting and Current attribute. I'm not really sure what the complication is, however - perhaps I've missed something else?
I'd do it with a single for-each (or more likely apply-templates but I'll follow your current structure) so you can use position() to generate the indexes. In order to put all the Male employees first followed by the Female ones it's sufficient to <xsl:sort> the for-each in descending order of Gender (because "M" is later in the alphabet than "F"):
<?xml version="1.0" encoding="UTF-8"?>
<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="Data">
<Employees>
<xsl:for-each select="Employees/Employee">
<xsl:sort select="Gender" order="descending" />
<Employee Gender="{Gender}" index="{position()}">
<xsl:attribute name="Current">
<xsl:choose>
<xsl:when test="position() = 1">true</xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<Name>
<xsl:value-of select="EmployeeName"/>
</Name>
</Employee>
</xsl:for-each>
</Employees>
</xsl:template>
</xsl:stylesheet>

how to select specific node sets in xslt

The xslt I'm currently using generates all the tags on the root.
I need to get the <row> sets and <config> set.
Source Xml:
<root>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<items>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</items>
<tenders>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</tenders>
<taxes>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</taxes>
</root>
Attempted Xslt:
<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="row/*">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Desired Output:
<root>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</root>
This short and simple transformation:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match=
"node()[not(self::root or ancestor-or-self::config or ancestor-or-self::row)]">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<items>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</items>
<tenders>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</tenders>
<taxes>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</taxes>
</root>
produces the wanted, correct result:
<root>
<config>
<postdate>2011-03-30</postdate>
<location>84</location>
<meal>07:36</meal>
<checknumber>91339082011-03-30T07:36:12</checknumber>
</config>
<row>
<descriptor>7297364</descriptor>
<qty>1</qty>
<price>33</price>
<value>33</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<descriptor>7794473</descriptor>
<qty>1</qty>
<price>60</price>
<value>60</value>
<recordtype>1</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>13</id>
<value>117.99</value>
<recordtype>2</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
<row>
<id>2</id>
<value>8.25</value>
<recordtype>3</recordtype>
<postdate>2011-03-30</postdate>
<location>84</location>
</row>
</root>
Explanation:
Using and overriding the identity rule.
Proper use of the ancestor-or-self:: axis.
I figured it. This xslt works for me.
<xsl:stylesheet version="1.0" exclude-result-prefixes="msxsl" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/">
<root>
<xsl:for-each select="//row">
<row>
<xsl:apply-templates/>
</row>
</xsl:for-each>
<xsl:for-each select="//config">
<config>
<xsl:apply-templates/>
</config>
</xsl:for-each>
</root>
</xsl:template>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>