How to assign a unique id to the Parent, child and grandchild element? - xslt-1.0

I'm new to xslt and it would be really helpful if anyone could help me with this problem.
I have a XML document which looks something like this,
<ParentElement>
<ChildElement name = "xxx" place = "yyy">
<GrandChildElement parameter1 = "a" parameter2 = "b">
</GrandchildElement>
<GrandChildElement parameter1 = "c" parameter2 = "d">
</GrandchildElement>
</ChildElement>
<ChildElement name = "xxx" place = "yyy">
<GrandChildElement parameter1 = "a" parameter2 = "x">
</GrandchildElement>
<GrandChildElement parameter1 = "c" parameter2 = "y">
</GrandchildElement>
</ChildElement>
</ParentElement>
I want to assign a unique id to each child element and to each grandchilds. The output that I want should be something like this,
<ParentElement>
<ChildElement>
<Child_ID>100</Child_ID>
<Name></Name>
<GrandChild>
<id>200</id>
<Label></Label>
</GrandChild>
<GrandChild>
<id>201</id>
<Label></Label>
</GrandChild>
<GrandChild>
<id>202</id>
<Label></Label>
</GrandChild>
</ChildElement>
<ChildElement>
<Child_ID>101</Child_ID>
<Name></Name>
<GrandChild>
<id>203</id>
<Label></Label>
</GrandChild>
<GrandChild>
<id>204</id>
<Label></Label>
</GrandChild>
</ChildElement>
</ParentElement>
I used something this to create the ids for the Child elements which was successful.
<xsl:for-each select="ChildElement">
<xsl:variable name="i" select="1" />
<xsl:variable name="j" select="$i + position()"/>
<xsl:element name = "ChildElement">
<xsl:element name = "Id">DT-<xsl:value-of select="200 + $j"/></xsl:element>
</xsl:for-each>
what can I do to get a different id for every grand child?

XSLT has a built-in function for generating a unique id for each node in the input XML. Consider the following example:
XML
<ParentElement>
<ChildElement name = "xxx" place = "yyy">
<GrandChildElement parameter1 = "a" parameter2 = "b"/>
<GrandChildElement parameter1 = "c" parameter2 = "d"/>
</ChildElement>
<ChildElement name = "xxx" place = "yyy">
<GrandChildElement parameter1 = "a" parameter2 = "x"/>
<GrandChildElement parameter1 = "c" parameter2 = "y"/>
</ChildElement>
</ParentElement>
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="ParentElement">
<xsl:copy>
<xsl:for-each select="ChildElement">
<xsl:copy>
<Child_ID>
<xsl:value-of select="generate-id()"/>
</Child_ID>
<xsl:for-each select="GrandChildElement">
<GrandChild>
<id>
<xsl:value-of select="generate-id()"/>
</id>
</GrandChild>
</xsl:for-each>
</xsl:copy>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
The exact result varies from one processor to another. For example, libxslt may return a result like:
<?xml version="1.0" encoding="UTF-8"?>
<ParentElement>
<ChildElement>
<Child_ID>idm49868912656</Child_ID>
<GrandChild>
<id>idm49868912048</id>
</GrandChild>
<GrandChild>
<id>idm49868911344</id>
</GrandChild>
</ChildElement>
<ChildElement>
<Child_ID>idm49868910448</Child_ID>
<GrandChild>
<id>idm49868909744</id>
</GrandChild>
<GrandChild>
<id>idm49868909008</id>
</GrandChild>
</ChildElement>
</ParentElement>
while Xalan will produce a result like:
<?xml version="1.0" encoding="UTF-8"?><ParentElement>
<ChildElement>
<Child_ID>N10004</Child_ID>
<GrandChild>
<id>N10008</id>
</GrandChild>
<GrandChild>
<id>N1000C</id>
</GrandChild>
</ChildElement>
<ChildElement>
<Child_ID>N10011</Child_ID>
<GrandChild>
<id>N10015</id>
</GrandChild>
<GrandChild>
<id>N10019</id>
</GrandChild>
</ChildElement>
</ParentElement>

So, if you are looking for the pattern you showed in your input XML, this may be one approach. You have to consider what happens when you have more than 100 ChildElement elements. Just play with your offsets. This approach may be useful if you don't want letters in your id. Or your are dealing with multiple XMLs. Just pass a parameter to your XSLT file with your unique starting offset.
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxml="urn:schemas-microsoft-com:xslt">
<xsl:output indent="yes"/>
<xsl:variable name="childIDs">
<xsl:for-each select="//ChildElement">
<xsl:element name="element">
<xsl:element name="id">
<xsl:value-of select="generate-id(.)"/>
</xsl:element>
<xsl:element name="outid">
<xsl:value-of select="position() + 100"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:variable>
<!-- For 1.0 use your own prefix here. It may not be msxml. -->
<xsl:variable name="childIDList" select="msxml:node-set($childIDs)"/>
<xsl:variable name="grandchildIDs">
<xsl:for-each select="//GrandChildElement">
<xsl:element name="element">
<xsl:element name="id">
<xsl:value-of select="generate-id(.)"/>
</xsl:element>
<xsl:element name="outid">
<xsl:value-of select="position() + 200"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:variable>
<!-- For 1.0 use your own prefix here. It may not be msxml. -->
<xsl:variable name="grandchildIDList" select="msxml:node-set($grandchildIDs)"/>
<xsl:template match="ChildElement">
<xsl:variable name="id" select="generate-id(.)"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:element name="Child_ID">
<xsl:value-of select="$childIDList/element[id = $id]/outid"/>
</xsl:element>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="GrandChildElement">
<xsl:variable name="id" select="generate-id(.)"/>
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:element name="id">
<xsl:value-of select="$grandchildIDList/element[id = $id]/outid"/>
</xsl:element>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Related

XSLT 1.0 Move of one Segment

please tell me, how can i move the Segment "BGM" from "CNT" to "GRP"?
Actually XML:
<?xml version="1.0"?>
<SEEDELFOR>
<AG/>
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME> </INTERCHANGE_CONTROL_NUMBER>
</SENDER>
</SENDER_QUALIFIER>
</RECEIVER>
</RECEIVER_QUALIFIER>
</SYNTAX_IDENTIFIER>
</SYNTAX_VERSION>
<BGM>227</BGM>
<GRP>
</IDENTIFIER_BY>
</IDENTIFIER_SU>
</DATE_4>
</REF_ON>
</GRP>
</CNT>
</SEEDELFOR>
Correct output of my XML Should be:
<?xml version="1.0"?>
<SEEDELFOR>
<AG/>
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME>
<SENDER></SENDER>
</SENDER_QUALIFIER>
</RECEIVER>
</RECEIVER_QUALIFIER>
</SYNTAX_IDENTIFIER>
</SYNTAX_VERSION>
<GRP>
<BGM>227</BGM>
</GRP>
</CNT>
</SEEDELFOR>
Here´s my XSLT: I tried to copy, but i think it´s better to move the segment?
Edit: Thats my full XSLT: Maybe i have to cominate with other copy segments?
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--Copy all of CNT != BGM-->
<xsl:template match="CNT">
<xsl:copy>
<xsl:apply-templates select="*[local-name()!='BGM']" />
</xsl:copy>
</xsl:template>
<!--copy BGM from CNT to GRP-->
<xsl:template match="GRP">
<xsl:element name="GRP">
<xsl:apply-templates />
<xsl:copy-of select="../BGM" />
</xsl:element>
</xsl:template>
<!--copy the data from ADD/CONTACT with Qualifier EM to GRP, so it can be used in the mapping to set EMAIL from Customer-->
<xsl:template match="GRP">
<xsl:copy>
<xsl:for-each select ="./ADD/CONTACT">
<xsl:if test="./QUALIFIER='EM'">
<CONTACT_EMAIL>
<xsl:value-of select="NUMBER"/>
</CONTACT_EMAIL>
</xsl:if>
<xsl:if test="./QUALIFIER='TE'">
<CONTACT_TEL>
<xsl:value-of select="NUMBER"/>
</CONTACT_TEL>
</xsl:if>
<xsl:if test="./QUALIFIER='FX'">
<CONTACT_FAX>
<xsl:value-of select="NUMBER"/>
</CONTACT_FAX>
</xsl:if>
</xsl:for-each>
<!--copy all other nodes-->
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!--delete IC node-->
<xsl:template match="IC" />
<!--delete FILTER_SET node-->
<xsl:template match="FILTER_SET" />
<!--delete SEE_AG node-->
<xsl:template match="SEE_AG" />
<!--delete ME node-->
<xsl:template match="ME" />
<!--delete ADD node-->
<xsl:template match="ADD" />
<!-- delete segment (ADD) with specified QUALIFER (SU) -->
<xsl:template match="ADD[QUALIFIER='SU']"/>
<!--delete TRANSPORT_DETAILS node-->
<xsl:template match="TRANSPORT_DETAILS" />
<!--delete PACKAGE_DETAILS node-->
<xsl:template match="PACKAGE_DETAILS" />
<!--delete AMOUNT_DETAILS node-->
<xsl:template match="AMOUNT_DETAILS" />
<!--delete IC node-->
<xsl:template match="CONTACT" />
<!-- delete empty nodes -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[not(#*|*|comment()|processing-instruction()) and normalize-space()='']"/>
<!-- delete empty nodes -->
</xsl:stylesheet>
Thanks for your help.
Best regards
Julian
The idiomatic approach to "moving" a node from one place to another is:
copy everything as is,
except the node in question, and
copy the node to its new place:
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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<!-- remove BGM -->
<xsl:template match="BGM"/>
<xsl:template match="GRP">
<xsl:copy>
<xsl:apply-templates/>
<!-- add BGM -->
<xsl:copy-of select="../BGM" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
If you need to modify other nodes, add templates matching them as necessary.
Seems your XML is not well formed. If I have got it right then you can find below xslt to achieve your requirement:
XML can be:
<?xml version="1.0"?>
<SEEDELFOR>
<AG />
<CNT>
<TRANSMISSION_DATE>20190307</TRANSMISSION_DATE>
<TRANSMISSION_TIME>113300</TRANSMISSION_TIME>
<INTERCHANGE_CONTROL_NUMBER />
<SENDER />
<SENDER_QUALIFIER />
<RECEIVER />
<RECEIVER_QUALIFIER />
<SYNTAX_IDENTIFIER />
<SYNTAX_VERSION />
<BGM>227</BGM>
<GRP>
<IDENTIFIER_BY />
<IDENTIFIER_SU />
<DATE_4 />
<REF_ON />
</GRP>
</CNT>
</SEEDELFOR>
An the solution would be:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="*">
<xsl:copy>
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="CNT">
<xsl:copy>
<xsl:apply-templates select="*[local-name()!='BGM']" />
</xsl:copy>
</xsl:template>
<xsl:template match="GRP">
<xsl:element name="GRP">
<xsl:apply-templates />
<xsl:copy-of select="../BGM" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
http://xsltransform.net/eieE3Q5
<!-- remove BGM at CNT-->
<xsl:template match="BGM"/>
<!--copy the data from ADD/CONTACT with Qualifier EM to GRP, so it can be used in the mapping to set EMAIL from Customer-->
<xsl:template match="GRP">
<xsl:copy>
<xsl:for-each select ="./ADD/CONTACT">
<xsl:if test="./QUALIFIER='EM'">
<CONTACT_EMAIL>
<xsl:value-of select="NUMBER"/>
</CONTACT_EMAIL>
</xsl:if>
<xsl:if test="./QUALIFIER='TE'">
<CONTACT_TEL>
<xsl:value-of select="NUMBER"/>
</CONTACT_TEL>
</xsl:if>
<xsl:if test="./QUALIFIER='FX'">
<CONTACT_FAX>
<xsl:value-of select="NUMBER"/>
</CONTACT_FAX>
</xsl:if>
</xsl:for-each>
<!--copy BGM from CNT to GRP -->
<xsl:copy-of select="../BGM" />
<!--copy all other nodes-->
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>

Dynamically update text nodes in XSLT

I'm trying to update the text nodes in xml based on the check if it matches a certain pattern
in xslt 1.0
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:regexp="http://exslt.org/regular-expressions">
<xsl:output method="xml" version="1.0"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text()">
<xsl:copy>
<xsl:call-template name="CheckAndReplace">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="pattern" select=""/>
</xsl:call-template>
</xsl:copy>
</xsl:template>
<xsl:template name="CheckAndReplace">
<xsl:param name="text"/>
<xsl:param name="pattern"/>
<xsl:for-each select="regexp:match( $text, $pattern, 'gi' )">
<xsl:copy-of select="regexp:replace( $text, $pattern, 'gi','*' )"
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
XML:
<Root>
<Name> Kabir </Name>
<Id>  </Id>
</Root>
Here the request has ID tag which matches my pattern and that needs to be replaced
Result Required:
<Root>
<Name> Kabir </Name>
<Id> * </Id>
</Root>
There is no regex in XSLT 1.0. If your goal is to replace all occurrences of the  character with a * then try:
<xsl:template match="text()">
<xsl:value-of select="translate(., '', '*')" />
</xsl:template>

XSL Replace an element by splitting it's values

I have the following XML which contains the order details in single element.
<?xml version="1.0" encoding="UTF-8"?>
<MESSAGE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MESSAGE_ID>361155</MESSAGE_ID>
<NAME>Header123</NAME>
<HEADERS>
<HEADER>
<ACTION_COUNT>1</ACTION_COUNT>
<ACTION_DESC>
<DAILY_LOAD>
<LOAD_NO>1</LOAD_NO>
<CUSTOMER_NO>09656951</CUSTOMER_NO>
<REFERENCE xsi:nil="1"/>
<SERIAL>450255470000403803</SERIAL>
<ORDERS>
<ORDER>
<ITEM>5000128762885</ITEM>
<DETAILS>4582;Robert and Co;8;5526;SWD</DETAILS>
<EMP_NO>13</EMP_NO>
<USE_BY_DATE>20110611</USE_BY_DATE>
<DEPOT>5000128910035</DEPOT>
</ORDER>
<ORDER>
<ITEM>5000128853613</ITEM>
<DETAILS>5000;Anne and Co;9;2020;ATM</DETAILS>
<EMP_NO>5</EMP_NO>
<USE_BY_DATE>20110613</USE_BY_DATE>
<DEPOT>5000128910035</DEPOT>
</ORDER>
</ORDERS>
</DAILY_LOAD>
</ACTION_DESC>
</HEADER>
</HEADERS>
<LIST_ID>23689</LIST_ID>
<TRAILER>TEST_NEEDED</TRAILER>
</MESSAGE>
I need that to be transformed into the following format.
(only the DAILY_LOAD>ORDERS section should be changed as below)
.....
<ORDERS>
<ORDER>
<ITEM>5000128762885</ITEM>
<ORDER_NO>4582</ORDER_NO>
<CUSTOMER>Robert and Co</CUSTOMER>
<NO_OF_ITEMS>8</NO_OF_ITEMS>
<TOTAL>5526</TOTAL>
<LOCATION>SWD</LOCATION>
<EMP_NO>13</EMP_NO>
<USE_BY_DATE>20110611</USE_BY_DATE>
<DEPOT>5000128910035</DEPOT>
</ORDER>
<ORDER>
<ITEM>5000128853613</ITEM>
<ORDER_NO>5000</ORDER_NO>
<CUSTOMER>Anne and Co</CUSTOMER>
<NO_OF_ITEMS>9</NO_OF_ITEMS>
<TOTAL>2020</TOTAL>
<LOCATION>ATM</LOCATION>
<EMP_NO>5</EMP_NO>
<USE_BY_DATE>20110613</USE_BY_DATE>
<DEPOT>5000128910035</DEPOT>
</ORDER>
</ORDERS>
.....
The transformed XML contains no but it's divided into CUSTOMER, NO_OF_ITEMS, TOTAL and LOCATION. Any suggestion or help will be highly appreciated.
Thank you in advance.
XSLT 1.0
Using tokenizeString template as suggested in Heber.it blog. I have modified the code for producing different tag names depending on recursion level (ORDER_NO, CUSTOMER, NO_OF_ITEMS, etc).
<?xml version="1.0" encoding="iso-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<!-- replaces order nodes -->
<xsl:template match="ORDER">
<ORDER>
<xsl:call-template name="tokenizeString">
<xsl:with-param name="list" select="./DETAILS"/>
<xsl:with-param name="delimiter" select="';'"/>
<xsl:with-param name="level" select="1"/>
</xsl:call-template>
</ORDER>
</xsl:template>
<!-- copy everything that doesn't match another template -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- template to tokenize strings -->
<xsl:template name="tokenizeString">
<!--passed template parameter -->
<xsl:param name="list"/>
<xsl:param name="delimiter"/>
<xsl:param name="level"/>
<xsl:choose>
<xsl:when test="contains($list, $delimiter)">
<xsl:choose>
<xsl:when test="$level = 1">
<ITEM>
<xsl:value-of select="ITEM"/>
</ITEM>
<ORDER_NO>
<xsl:value-of select="substring-before($list,$delimiter)"/>
</ORDER_NO>
</xsl:when>
<xsl:when test="$level = 2">
<CUSTOMER>
<xsl:value-of select="substring-before($list,$delimiter)"/>
</CUSTOMER>
</xsl:when>
<xsl:when test="$level = 3">
<NO_OF_ITEMS>
<xsl:value-of select="substring-before($list,$delimiter)"/>
</NO_OF_ITEMS>
</xsl:when>
<xsl:when test="$level = 4">
<TOTAL>
<xsl:value-of select="substring-before($list,$delimiter)"/>
</TOTAL>
</xsl:when>
</xsl:choose>
<xsl:call-template name="tokenizeString">
<!-- store anything left in another variable -->
<xsl:with-param name="list" select="substring-after($list,$delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
<xsl:with-param name="level" select="$level + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:choose>
<xsl:when test="$list = ''">
<xsl:text/>
</xsl:when>
<xsl:otherwise>
<LOCATION>
<xsl:value-of select="$list"/>
</LOCATION>
<EMP_NO>
<xsl:value-of select="EMP_NO"/>
</EMP_NO>
<USE_BY_DATE>
<xsl:value-of select="USE_BY_DATE"/>
</USE_BY_DATE>
<DEPOT>
<xsl:value-of select="DEPOT"/>
</DEPOT>
</xsl:otherwise>
</xsl:choose>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
XSLT 2.0
Using tokenize function you can do this:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template match="/">
<xsl:apply-templates/>
</xsl:template>
<!-- this template replaces order nodes -->
<xsl:template match="ORDER">
<ORDER>
<xsl:variable name="fields" select="tokenize(DETAILS, ';')"/>
<ITEM>
<xsl:value-of select="ITEM"/>
</ITEM>
<ORDER_NO>
<xsl:value-of select="$fields[position()=1]"/>
</ORDER_NO>
<CUSTOMER>
<xsl:value-of select="$fields[position()=2]"/>
</CUSTOMER>
<NO_OF_ITEMS>
<xsl:value-of select="$fields[position()=3]"/>
</NO_OF_ITEMS>
<TOTAL>
<xsl:value-of select="$fields[position()=4]"/>
</TOTAL>
<LOCATION>
<xsl:value-of select="$fields[position()=5]"/>
</LOCATION>
<EMP_NO>
<xsl:value-of select="EMP_NO"/>
</EMP_NO>
<USE_BY_DATE>
<xsl:value-of select="USE_BY_DATE"/>
</USE_BY_DATE>
<DEPOT>
<xsl:value-of select="DEPOT"/>
</DEPOT>
</ORDER>
</xsl:template>
<!-- this template copies everything that doesn't match another template -->
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

XSLT code for converting attributes to child elements and putting the value of current element into its another new child element

I am unaware of XSLT,Please help:
I have the following XML:
<OLifE>
<Holding id="1234">
<HoldingKey>1397650618090</HoldingKey>
<HoldingTypeCode tc="2">Policy</HoldingTypeCode>
<HoldingStatus tc="2">Inactive</HoldingStatus>
<CarrierAdminSystem>PAS</CarrierAdminSystem>
</Holding>
</OLifE>
I want the output like this:
<OLifE>
<Holding>
<id>1234</id>
<HoldingKey>1397650618090</HoldingKey>
<HoldingTypeCode>
<tc>2</tc>
<value>Policy</value>
</HoldingTypeCode>
<HoldingStatus>
<tc>2</tc>
<value>Inactive</value>
</HoldingStatus>
<CarrierAdminSystem>PAS</CarrierAdminSystem>
</Holding>
</OLifE>
please note that, all attributes are needed to be converted to child elements but, for elements that have a tc attribute specified:
<HoldingTypeCode tc="2">Policy</HoldingTypeCode>
need to be handled carefully.
I DO NOT want the output like:
<HoldingTypeCode>
<tc>2</tc>Policy</HoldingTypeCode>
I have the following XSLT code which needs the slight modification:
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml"/>
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:for-each select="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
Please help.
Assuming you want a generic solution, how about:
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="*"/>
<!-- modified identity transform -->
<xsl:template match="node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<!-- attributes to elements -->
<xsl:template match="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:template>
<!-- avoid mixed content -->
<xsl:template match="text()[../#*]">
<value>
<xsl:value-of select="."/>
</value>
</xsl:template>
</xsl:stylesheet>
Edit
However, the root element in my original XML has got xmlns="some web
address" attribute.
In such case, change the first template to:
<!-- modified identity transform -->
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:apply-templates select="#* | node()"/>
</xsl:element>
</xsl:template>
This is assuming you want all the output nodes to be in no namespace.
I think you can define a template like this to be called where you want to output the attributes :
<xsl:template name="transform.attrs">
<xsl:for-each select="#*">
<xsl:element name="{name()}">
<xsl:value-of select="."/>
</xsl:element>
</xsl:for-each>
</xsl:template>
For the most general case you use almost the same template as before, just call the new template for attributes:
<xsl:template match="*">
<xsl:element name="{name()}">
<xsl:call-template name="transform.attrs">
<xsl:apply-templates select="*|text()"/>
</xsl:element>
</xsl:template>
And for the specific elements, bearing the #tc attribute:
<xsl:template match="*[#tc]">
<xsl:element name="{name()}">
<xsl:call-template name="transform.attrs" />
<value>
<xsl:apply-templates select="*|text()"/>
</value>
</xsl:element>
</xsl:template>

How to pass a value from for-each to select from another node-set in XSL?

Given my input XML:
<?xml version="1.0" encoding="utf-8"?>
<FlightAvailability>
<FareDetails>
<Fare ID="2007" DepartureDate="2012-11-23T07:05:00">
<FareTypes>
<FareType FareType="Promo1">
<FareInfo Class="Y" FareBasis="Y" Fare="1500"/>
</FareType>
<FareType FareType="Promo2">
<FareInfo Class="Y" FareBasis="Y" Fare="1000"/>
</FareType>
</FareTypes>
</Fare>
<Fare ID="2008" DepartureDate="2012-11-23T08:00:00">
<FareTypes>
<FareType FareType="Promo1">
<FareInfo Class="Y" FareBasis="Y" Fare="2500"/>
</FareType>
<FareType FareType="Promo2">
<FareInfo Class="Y" FareBasis="Y" Fare="2000"/>
</FareType>
</FareTypes>
</Fare>
</FareDetails>
<SegmentDetails>
<Segment ID="2007" Origin="DEL" Destination="BOM"
DepartureDate="2012-11-23T07:05:00" Airline="YY" ArrivalDate="2012-11-23T08:55:00"
Stops="0" AircraftType="320"
FlightNum="100"/>
<Segment ID="2008" Origin="DEL" Destination="BOM"
DepartureDate="2012-11-23T08:00:00" Airline="YY" ArrivalDate="2012-11-23T09:55:00"
Stops="0" AircraftType="320" FlightNum="200"/>
</SegmentDetails>
</FlightAvailability>
I wanted to create an output XML wherein I pick up FareTypes of FareType "Promo1" and find the corresponding Segment by matching the ID (eg. 2007 in Fare and Segment tags). While I iterate for-each in the FareDetails/Fare and pick up the ID, how do I pass the ID and pick up the particular Segment information. I am new to XSL and know this should probably done making a call to template with-param, where the param is the ID, but not able to set it up. I tried to pass a variable, but it doesn't pick anything from the Segments.
This is an XSL I tried:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output version="1.0" method="xml" omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:element name="AvailRS">
<xsl:element name="Availability">
<xsl:for-each select="//FareDetails/Fare">
<xsl:element name="item">
<xsl:element name="Fares">
<xsl:variable name = "lfid" select="#ID"/>
<xsl:element name = "ID"><xsl:value-of select="$lfid"/></xsl:element>
<xsl:for-each select="FareTypes/FareType[#FareType='Promo1']">
<xsl:element name="item">
<xsl:element name="BaseAmount">
<xsl:value-of select="ceiling(FareInfo/#Fare)"/>
</xsl:element>
<xsl:element name="FareBasisCode">
<xsl:value-of select="FareInfo/#FareBasis"/>
</xsl:element>
<xsl:element name="FareClass">
<xsl:value-of select="FareInfo/#Class"/>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
<xsl:element name="Flights">
<xsl:variable name="seg" select="//SegmentDetails/Segment[#ID='$lfid']"/>
<xsl:element name="item">
<xsl:element name="FlightNumber"><xsl:value-of select="$seg/#FlightNum"/></xsl:element>
<xsl:element name="DepTime"><xsl:value-of select="$seg/#DepartureDate"/></xsl:element>
<xsl:element name="ArrTime"><xsl:value-of select="$seg/#ArrivalDate"/></xsl:element>
<xsl:element name="Origin"><xsl:value-of select="$seg/#Origin"/></xsl:element>
<xsl:element name="Destination"><xsl:value-of select="$seg/#Destination"/></xsl:element>
<xsl:element name="Stops"><xsl:value-of select="$seg/#Stops"/></xsl:element>
<xsl:element name="Aircraft"><xsl:value-of select="$seg/#AircraftType"/></xsl:element>
</xsl:element>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:element>
<Status>Success</Status>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This doesn't pick the data from Segment:
<AvailRS>
<Availability>
<item>
<Fares>
<ID>2007</ID>
<item>
<BaseAmount>1500</BaseAmount>
<FareBasisCode>Y</FareBasisCode>
<FareClass>Y</FareClass>
</item>
</Fares>
<Flights>
<item>
<FlightNumber></FlightNumber>
<DepTime></DepTime>
<ArrTime></ArrTime>
<Origin></Origin>
<Destination></Destination>
<Stops></Stops>
<Aircraft></Aircraft>
</item>
</Flights>
</item>
<item>
<Fares>
<ID>2008</ID>
<item>
<BaseAmount>2500</BaseAmount>
<FareBasisCode>Y</FareBasisCode>
<FareClass>Y</FareClass>
</item>
</Fares>
<Flights>
<item>
<FlightNumber></FlightNumber>
<DepTime></DepTime>
<ArrTime></ArrTime>
<Origin></Origin>
<Destination></Destination>
<Stops></Stops>
<Aircraft></Aircraft>
</item>
</Flights>
</item>
</Availability>
<Status>Success</Status>
</AvailRS>
What I would like to get in the above example would be the Segment information corresponding to the Fare connected by the ID attribute (2007, 2008), but the $id variable isn't going to work. How do I select the corresponding ID?
You need to remove the quotes around the variable reference. Change
//SegmentDetails/Segment[#ID='$lfid']
to
//SegmentDetails/Segment[#ID = $lfid]
Otherwise, you are asking XSLT to compare the value of the ID attribute with the literal string $lfid.