Extract Delimited Data in a string input - xslt

I am trying to convert a string which is delimited by a ";"
Here are the possible inputs that i have
Possibility 1: <String>1;2;3;4;5;6;7;8;9;10;11</String>
Possibility 2: <String>1;2;3;4;5</String>
Possibility 3: <String>1;2;3;4;5;6;7;8;9</String>
Possibility 4: <String>1;2;3;4</String>
Possibility 5: <String>1;2;3;4;5;;;;;10;11</String>
Few points concerning the logic
Would like to have the above delimited strings mapped to individual
fields like shown below.
Not concerned about the values in position 6
and 7 and hence they are not seen in any of the outputs
Towards the
end of the output is an element with name "combined" which is a
concatenation of values received in string delimited by an "_"
Order of concatenation though is slightly different which is mentioned here
1_2_3_4_5_8_11_9_10
If a value is not present in input string then it remains blank like 1__3_4_5__11__10
Output that i would like to see for each of above mentioned possibilities is
Possibility 1:
<A>1</A>
<B>2</B>
<C>3</C>
<D>4</D>
<E>5</E>
<F>8</F>
<G>9</G>
<H>10</H>
<I>11</I>
<Combined>1_2_3_4_5_8_11_9_10</Combined>
Possibility 2:
<A>1</A>
<B>2</B>
<C>3</C>
<D>4</D>
<E>5</E>
<F></F>
<G></G>
<H></H>
<I></I>
<Combined>1_2_3_4_5____</Combined>
Possibility 3:
<A>1</A>
<B>2</B>
<C>3</C>
<D>4</D>
<E>5</E>
<F>8</F>
<G>9</G>
<H></H>
<I></I>
<Combined>1_2_3_4_5_8__9_</Combined>
Possibility 4:
<A>1</A>
<B>2</B>
<C>3</C>
<D>4</D>
<E></E>
<F></F>
<G></G>
<H></H>
<I>11</I>
<Combined>1_2_3_4_____</Combined>
Possibility 5:
<A>1</A>
<B>2</B>
<C>3</C>
<D>4</D>
<E>5</E>
<F></F>
<G></G>
<H>10</H>
<I>11</I>
<Combined>1_2_3_4_5__11__10</Combined>
I am able to get the desired output if i assume that string is of fixed length with 11 delimited sets and this i was able to achieve by using substring before and after functions
However for possibilities like 2 3 4 provider is not always sending the empty delimiters and then my xsl doesnt seem to work.
Any help in this context please?

If you are using Xalan, then this should work for you:
<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:template match="/">
<xsl:variable name="tokens" select="str:split(String, ';')" />
<root>
<A>
<xsl:value-of select="$tokens[1]"/>
</A>
<B>
<xsl:value-of select="$tokens[2]"/>
</B>
<C>
<xsl:value-of select="$tokens[3]"/>
</C>
<D>
<xsl:value-of select="$tokens[4]"/>
</D>
<E>
<xsl:value-of select="$tokens[5]"/>
</E>
<F>
<xsl:value-of select="$tokens[8]"/>
</F>
<G>
<xsl:value-of select="$tokens[9]"/>
</G>
<H>
<xsl:value-of select="$tokens[10]"/>
</H>
<I>
<xsl:value-of select="$tokens[11]"/>
</I>
<Combined>
<xsl:value-of select="$tokens[1]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[2]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[3]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[4]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[5]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[8]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[11]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[9]"/>
<xsl:text>_</xsl:text>
<xsl:value-of select="$tokens[10]"/>
</Combined>
</root>
</xsl:template>
</xsl:stylesheet>

Related

How get String before hyphen in XSLT

I want yo get the first name initial and last name.
Input :
<root>
<ele str="Great Success-Turbo Pat(by Sundon) [Cheeky Pat 8m(Monarchy)"/>
<ele str="Brylin Boyz-Scallywag"/>
<ele str="Majestic Son-Be Bee"/>
</root>
Output
<names>Great Success</name>
<names>Brylin Boyz</name>
<names>Majestic Son</name>
Tried Code:
<xsl:template match="root/name">
<names>
<xsl:value-of select="#str" />
</name>
</xsl:template>
I am using XSLT 2.0. Thank you
You need to use fn:substring-before() :
<xsl:template match="ele">
<name>
<xsl:value-of select="substring-before(#str, '-')" />
</name>
</xsl:template>

XML to pipe-delimited - How to copy/duplicate data to different rows

I'm new to XSLT and trying to transform from XML to pipe-delimited format. The problem that I'm having is that in the output, each claim has to be duplicated for each service line.
Expected Output:
EP030315706890704|TESTSUBMITTER|FAMILY HEALTHCARE|1122334455|1|99214|179.00
EP030315706890704|TESTSUBMITTER|FAMILY HEALTHCARE|1122334455|2|2000F|0.00
EP030315706890705|TESTSUBMITTER2|FAMILY HEALTHCARE|1122334455|1|99214|179.00
EP030315706890705|TESTSUBMITTER2|FAMILY HEALTHCARE|1122334455|2|2000F|0.00
Input XML looks as follows:
<payloadContainer>
<afile>
<clm>
<hdr>
<corn>EP030315706890704</corn>
<idSend>112233445</idSend>
<nmSend>TESTSUBMITTER</nmSend>
</hdr>
<provBill>
<name>
<nmOrg>FAMILY HEALTHCARE</nmOrg>
</name>
<id T="XX" P="P">1122334455</id>
</provBill>
<serv S="1">
<numLine>1</numLine>
<prof>
<px L="S">
<cdPx T="HC">99214</cdPx>
</px>
<amtChrg>179.00</amtChrg>
</prof>
</serv>
<serv S="2">
<numLine>2</numLine>
<prof>
<px L="S">
<cdPx T="HC">2000F</cdPx>
</px>
<amtChrg>0.00</amtChrg>
</prof>
</serv>
</clm>
<clm>
<hdr>
<corn>EP030315706890705</corn>
<idSend>112233445</idSend>
<nmSend>TESTSUBMITTER2</nmSend>
</hdr>
<provBill>
<name>
<nmOrg>FAMILY HEALTHCARE</nmOrg>
</name>
<id T="XX" P="P">1122334455</id>
</provBill>
<serv S="1">
<numLine>1</numLine>
<prof>
<px L="S">
<cdPx T="HC">99214</cdPx>
</px>
<amtChrg>179.00</amtChrg>
</prof>
</serv>
<serv S="2">
<numLine>2</numLine>
<prof>
<px L="S">
<cdPx T="HC">2000F</cdPx>
</px>
<amtChrg>0.00</amtChrg>
</prof>
</serv>
</clm>
</afile>
</payloadContainer>
Desired output XML:
<Table>
<row>
.... All the fields represented here.
</row>
</Table>
Possible solution: https://www.dropbox.com/s/wzvtzw7ihtgxx9o/claimtoRedshift.xsl
This scenario creates two row's dynamically. However, I'm still stuck at how to duplicate for each service line.
I don't see the connection of the linked XSLT to the question presented here. AFAICT, the following stylesheet will return the expected pipe-delimited output:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="UTF-8"/>
<xsl:template match="/payloadContainer">
<xsl:for-each select="afile/clm">
<xsl:variable name="common">
<xsl:value-of select="hdr/corn"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="hdr/nmSend"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="provBill/name/nmOrg"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="provBill/id"/>
<xsl:text>|</xsl:text>
</xsl:variable>
<xsl:for-each select="serv">
<xsl:value-of select="$common"/>
<xsl:value-of select="numLine"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="prof/px/cdPx"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="prof/amtChrg"/>
<xsl:text>
</xsl:text>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

updating an xml from another one using ids

I have to pick text from translation.xml and update skeleton.xml's respective (where #xid =#id) nodes . in other words IF #id of translation.xml matches with #xid of skeleton.xml, the text content should be placed under skeleton.xml (+ if there is child element present the text content (inline element i, b, etc) also should go in respective parent element: Please see example below for more details:
Note: the inline element could be anything in skeleton.xml (its not restrict to b or i, so it should be generic)
skeleton.xml
<root>
<para a="b" b="c">
<text xid="1">This is first para <b xid="2" a="c" b="d">This is bold <i xid="3" b="d" c="e">This is italics</i> rest of bold</b> rest of para</text>
</para>
<para><text xid="4">This is second para</text></para>
<para><text xid="5">This is unchanged para</text></para>
<para><text xid="6">This is unchanged para</text></para>
</root>
translation.xml
<root>
<TU id="1">
<source>This is first para <g id="2" tagName="b">This is bold <g id="3" tagName="i">This is italics</g> rest of bold</g> rest of para</source>
<target>suum primum para <g id="2" tagName="b">Et hoc confidens, <g id="3" tagName="i">Hoc est, Te Deum</g> Reliqua autem audet,</g> reliqua autem verba haec</target>
</TU>
<TU id="4">
<source>This is second para</source>
<target>Hoc est secundum verba haec</target>
</TU>
</root>
UpdatedSkeleton.xml
<root>
<para a="b" b="c">
<text xid="1">suum primum para <b xid="2" a="c" b="d">Et hoc confidens, <i xid="3" b="d" c="e">Hoc est, Te Deum</i> Reliqua autem audet,</b> reliqua autem verba haec</text>
</para>
<para><text xid="4">Hoc est secundum verba haec</text></para>
<para><text xid="5">This is unchanged para</text></para>
<para><text xid="6">This is unchanged para</text></para>
</root>
I am trying with this code, but facing challenge to place text of inline content at the right place:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:param name="translation" select="'file:/C:/Skeleton.xml'"></xsl:param>
<xsl:variable name="doc">
<xsl:copy-of select="doc($translation)"/>
</xsl:variable>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="text">
<xsl:variable name="skelID" select="#xid"/>
<xsl:choose>
<xsl:when test="$doc//*[$skelID=#id]">
<xsl:apply-templates select="$doc//*[$skelID=#id]/target"/>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
You can copy the <text> node adding <xsl:copy> to your test block, and copy the xid attribute using <xsl:attribute>. For the unmodified nodes, you can use <xsl:copy-of> which copies the full tree, including attributes:
<xsl:when test="$doc//*[$skelID=#id]">
<xsl:copy>
<xsl:attribute name="xid">
<xsl:value-of select="$skelID"/>
</xsl:attribute>
<xsl:apply-templates select="$doc//*[$skelID=#id]/target"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
That will also copy the <target> element. You can remove it adding a template:
<xsl:template match="target">
<xsl:apply-templates/>
</xsl:template>
You also aren't replacing the <g> tag. I assume the #tagName attribute says what it's supposed to be transformed into. This should do it:
<xsl:template match="g">
<xsl:element name="{#tagName}">
<xsl:attribute name="xid">
<xsl:value-of select="#id"/>
</xsl:attribute>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
I might have missed something, but that will probably solve most of your problem.

xslt wrapping duplicate lines case insensitive inside a for-each

I am trying to write a loop using XSLT so that it automatically groups all items with the same ID but in a case insensitive way. Unfortunately the data that I am trying to parse through is client driven so I cannot change it prior to load.
regardless here is a XML structure...
<Document>
<Row>
<Cell>ID</Cell>
</Row>
<Row>
<Cell>hi</Cell>
</Row>
<Row>
<Cell>Hi</Cell>
</Row>
<Row>
<Cell>Hello</Cell>
</Row>
<Row>
<Cell>Hello</Cell>
</Row>
<Row>
<Cell>Hola</Cell>
</Row>
</Document>
This is the XSLT I am currently using...
<xsl:template match="Document">
<NewDocument xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<xsl:for-each select="//Row[position() > 1]/Cell[1][not(.=preceding::Row/Cell[1])]">
<xsl:variable name="currentOrderID" select="." />
<xsl:variable name="currentOrderGroup" select="//Row[Cell[1] = $currentOrderID]" />
<MainID>
<xsl:value-of select="$currentOrderGroup[1]/Cell[1]"/>
</MainID>
<IDs>
<xsl:for-each select="$currentOrderGroup">
<id>
<xsl:value-of select="Cell[1]"/>
</id>
</xsl:for-each>
</IDs>
</xsl:for-each>
</NewDocument>
</xsl:template>
This is just wrapping up things as expected in a CaSe SeNSiTiVe way...
I've been trying to use a translate in there in order to make everything uppercase, however I can't seem to get the syntax just right.
The result I am trying to achieve here is this:
<NewDocument>
<MainID>hi</MainID>
<IDs>
<id>hi</id>
<id>Hi</id>
</IDs>
<MainID>Hello</MainID>
<IDs>
<id>Hello</id>
<id>Hello</id>
</IDs>
<MainID>Hola</MainID>
<IDs>
<id>Hola</id>
</IDs>
</NewDocument>
Can't seem to find anything specifically for what I need.
Thanks!
In XSLT1.0, to convert strings to lower case you need to use the rather cumbersome translate function in xpath.
translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')
Furthermore, your problem is one of grouping, and in XSLT1.0 that usually means a technique known as Meunchian Grouping. To do, this you first define a key to look up items in the groups you require
<xsl:key
name="Cell"
match="Cell"
use="translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
Here we are looking up cells based on their (lower-case) text content.
To find the first element in each group, you look for Cell elements in the XML which also happen to be the first element occurring in your look-up key
<xsl:apply-templates
select="Row/Cell
[generate-id()
= generate-id(
key('Cell',
translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))[1])]"/>
Then, when you match the first element, you can then match all elements within the group by looking at the key.
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="Cell" match="Cell" use="translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')"/>
<xsl:template match="Document">
<NewDocument>
<xsl:apply-templates select="Row/Cell[generate-id() = generate-id(key('Cell', translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))[1])]"/>
</NewDocument>
</xsl:template>
<xsl:template match="Cell">
<MainID>
<xsl:value-of select="."/>
</MainID>
<IDs>
<xsl:apply-templates select="key('Cell', translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'))" mode="group"/>
</IDs>
</xsl:template>
<xsl:template match="Cell" mode="group">
<id>
<xsl:value-of select="."/>
</id>
</xsl:template>
</xsl:stylesheet>
Note the use of the mode attribute, to distinguish between the two templates matching Cell elements.
When applied to your XML, the following is output:
<NewDocument>
<MainID>ID</MainID>
<IDs>
<id>ID</id>
</IDs>
<MainID>hi</MainID>
<IDs>
<id>hi</id>
<id>Hi</id>
</IDs>
<MainID>Hello</MainID>
<IDs>
<id>Hello</id>
<id>Hello</id>
</IDs>
<MainID>Hola</MainID>
<IDs>
<id>Hola</id>
</IDs>
</NewDocument>
Note, I wasn't sure what to do with the Cell with ID as a value, so I left that it in. If you do want to exclude it, just add this line to the XSLT
<xsl:template match="Cell[. = 'ID']" />

XSLT for-each with complex condition

I am transforming following XML to generate HTML.
XML
<clause code="section6">
<variable col="1" name="R1C1" row="1">Water</variable>
<variable col="2" name="R1C2" row="1">true</variable>
<variable col="1" name="R2C1" row="2">Gas</variable>
<variable col="2" name="R2C2" row="2"></variable>
<variable col="1" name="R3C1" row="3">Petrol</variable>
<variable col="2" name="R3C2" row="3">true</variable>
<clause>
XSLT
1: <xsl:for-each select="$clause/variable[#col='1']">
2: <xsl:sort select="#row" data-type="number"/>
3: <xsl:variable name="row-id" select="#row"/>
4: <xsl:variable name="row" select="$clause/variable[#row=$row-id]"/>
5: <xsl:if test="$clause/variable[#col='2' and #row=$row-id]='true'">
6: <xsl:value-of name="row-no" select="concat(position(), ') ')"/>
7: <xsl:value-of select="$clause/variable[#col='1' and #row=$row-id]"/>
8: </xsl:if>
9: </xsl:for-each>
The transformation works fine and shows result 1) Water 3) Petrol
The issue is sequence number. You can see condition on Line 5 filters rows that only have 'true' value in col 2 and position() used for displaying sequence number. I cannot have running counter in XLST.
I was wondering if I can add condition of Line 5 with for-each at Line 1. The result with above example should be 1) Water 2) Patrol any advice?
Does this do what you want?
I drive the for-each selection on col 2 being true. That way position, which equals where we are in the selected set of nodes will equal 2 not 3
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:for-each select="clause/variable[#col='2' and text()='true']">
<xsl:sort select="#row" data-type="number"/>
<xsl:variable name="row-id" select="#row"/>
<xsl:value-of select="concat(position(), ') ')"/>
<xsl:value-of select="/clause/variable[#col='1' and #row=$row-id]"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Though I'd probably use templates in preference to the for-each and use current() so that we don't need the row-id variable:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:template match="/">
<xsl:apply-templates select="clause/variable[#col='2' and text()='true']">
<xsl:sort select="#row" data-type="number"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="clause/variable[#col='2' and text()='true']">
<xsl:value-of select="concat(position(), ') ')"/>
<xsl:value-of select="/clause/variable[#col='1' and #row=current()/#row]"/>
</xsl:template>
</xsl:stylesheet>
I don't think you can express this with a single XPath 1.0 expression.
In XSLT 1.0 I will use keys and the solution becomes short, elegant and efficient:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kVarCol2" match="variable[#col=2]" use="#row"/>
<xsl:template match="/*">
<xsl:for-each select="variable[#col='1'][key('kVarCol2', #row)='true']">
<xsl:sort select="#row" data-type="number"/>
<xsl:value-of select="concat('
', position(), ') ', .)"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document (corrected to be made well-formed):
<clause code="section6">
<variable col="1" name="R1C1" row="1">Water</variable>
<variable col="2" name="R1C2" row="1">true</variable>
<variable col="1" name="R2C1" row="2">Gas</variable>
<variable col="2" name="R2C2" row="2"></variable>
<variable col="1" name="R3C1" row="3">Petrol</variable>
<variable col="2" name="R3C2" row="3">true</variable>
</clause>
the wanted, correct result is produced:
1) Water
2) Petrol
II. XPath 2.0 (single expression) solution:
for $i in 1 to max(/*/*/#row/xs:integer(.))
return
/*/variable[#row eq string($i)]
[#col eq '1'
and
../variable
[#row eq string($i)
and
#col eq '2'
and
. eq 'true'
]
]
/concat('
', position(), ') ', .)
When this XPath 2.0 expression is evaluated on the same XML document (above), the result is the same wanted string:
1) Water
2) Petrol