I've been trying to figure this our for a while, but no luck.
I have a for each loop specifically targeting to a node with specific attributes, but somehow the for each condition doesn't seem to work.
I have the following xml
<?xml version="1.0" encoding="UTF-8"?>
<application-template xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<config>
<env value="testing">
<subEnv value="env">
<group value="group2s">
<group value="group2as">
<prop value="group2Props">group2PropValues</prop>
</group>
</group>
</subEnv>
<subEnv value="misc1">
<group value="group2s">
<group value="group2as">
<prop value="group2Props">group2sPropValues</prop>
</group>
</group>
<group value="group2s1">
<group value="group2as1">
<prop value="group2Props">group2s1PropValues</prop>
</group>
</group>
</subEnv>
</env>
<env value="testingA">
<subEnv value="env">
<group value="test2">
<group value="test2a">
<group value="test2ab">
<prop value="group2Props">testingAGroup2PropValues</prop>
</group>
</group>
</group>
</subEnv>
</env>
</config>
</application-template>
with the following xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:param name="inputEnv"/>
<xsl:variable name="env" select="/application-template/config/env[#value=$inputEnv]"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text" indent="no"/>
<xsl:template match="/">
<xsl:call-template name="ou">
<xsl:with-param name="subEnvPos" select="$env/subEnv"/>
<xsl:with-param name="subEnvValue" select="$env/subEnv/#value"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="ou">
<xsl:param name="subEnvPos"/>
<xsl:param name="subEnvValue"/>
<xsl:variable name="test" select="/application-template/config/env[#value=$inputEnv]/subEnv[#value=$subEnvValue]"/>
<xsl:for-each select="$test/group">
testing1:
<xsl:value-of select="$test"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The param inputEnv is "testing" and the param $subEnvValue is "env". The output I got is
testing1:group2PropValues
testing1:group2PropValues
testing1:group2PropValues
testing1:group2PropValues
But I want to just loop once since the the condition is to match the subenv node = $subEnvValue (which is "env"). The output I'm hoping for is
testing1:group2PropValues
I think that instead of your current code:
<xsl:template match="/">
<xsl:call-template name="ou">
<xsl:with-param name="subEnvPos"
select="$env/subEnv"/>
<xsl:with-param name="subEnvValue"
select="$env/subEnv/#value"/>
</xsl:call-template>
</xsl:template>
you want something like this:
<xsl:template match="/">
<xsl:call-template name="ou">
<xsl:with-param name="subEnvPos"
select="$env/subEnv[1]"/>
<xsl:with-param name="subEnvValue"
select="$env/subEnv[1]/#value"/>
</xsl:call-template>
</xsl:template>
With this modification, the complete XSLT code now is:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:param name="inputEnv" select="'testing'"/>
<xsl:variable name="env" select=
"/application-template/config/env[#value=$inputEnv]"/>
<xsl:strip-space elements="*"/>
<xsl:output method="text" indent="no"/>
<xsl:template match="/">
<xsl:call-template name="ou">
<xsl:with-param name="subEnvPos"
select="$env/subEnv[1]"/>
<xsl:with-param name="subEnvValue"
select="$env/subEnv[1]/#value"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="ou">
<xsl:param name="subEnvPos"/>
<xsl:param name="subEnvValue"/>
<xsl:variable name="test" select=
"/application-template/config/env
[#value=$inputEnv]/subEnv[#value=$subEnvValue]"/>
<xsl:for-each select="$test/group">
testing1:
<xsl:value-of select="$test"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
and when applied to the provided XML document:
<application-template xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<config>
<env value="testing">
<subEnv value="env">
<group value="group2s">
<group value="group2as">
<group value="group2bs">
<prop value="group2Props">group2PropValues</prop>
</group>
</group>
</group>
</subEnv>
<subEnv value="misc1">
<group value="group2s">
<group value="group2as">
<group value="group2bs">
<prop value="group2Props">group2sPropValues</prop>
</group>
</group>
</group>
<group value="group2s1">
<group value="group2as1">
<group value="group2bs1">
<prop value="group2Props">group2s1PropValues</prop>
</group>
</group>
</group>
</subEnv>
<subEnv value="misc2">
<group value="group2sMisc2">
<group value="group2asMisc2">
<group value="group2bsMisc2">
<prop value="group2Props">group2PropValuesMisc2</prop>
</group>
</group>
</group>
</subEnv>
</env>
</config>
</application-template>
the wanted result is produced:
testing1:
group2PropValues
Update:
After the OP explained in a comment what he wants, here is a short and simple solution to the new problem:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:param name="vinputEnv" select="'testing'"/>
<xsl:template match="/*/*/env">
<xsl:if test="#value = $vinputEnv">
<xsl:for-each select="descendant::*[not(*)]">
<xsl:value-of select="concat('testing1: ', ., '
')"/>
</xsl:for-each>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied to the provided XML document (above), the wanted, correct result is produced:
testing1: group2PropValues
testing1: group2sPropValues
testing1: group2s1PropValues
testing1: group2PropValuesMisc2
Related
I've been stuck using the correct regex. I need to tokenize each data per keys along with there values. In my example below,
Sample File:
<Rec>
<Data>/CHG1/EUR1000,00/EXCH/0,10/CPRP/Payment Code 1</Data>
<Data>/CHG1/EUR1000,00/EXCH/0,10/CPRP/Payment Code 1</Data>
<Data>/CHG3/EUR3000,00/PURP//CD/Payment Code 3</Data>
<Data>/CHG5/EUR5000,00/PURP//PRTRY/Payment Code 5</Data>
<Data>/ORIG//CSID/EUR7000,00/BENM//ID/Payment Code 7</Data>
</Rec>
The keys with '//' in the middle is considered as 1 key. I need to generate the output like this:
<Data>
<Group>
<Token>/CHG1/EUR1000,00</Token>
<Token>/EXCH/0,10</Token>
<Token>/CPRP/Payment Code 1</Token>
</Group>
<Group>
<Token>/CHG3/EUR3000,00</Token>
<Token>/PURP//CD/Payment Code 3</Token>
</Group>
<Group>
<Token>/CHG5/EUR5000,00</Token>
<Token>/PURP//PRTRY/Payment Code 5</Token>
</Group>
<Group>
<Token>/ORIG//CSID/EUR7000,00</Token>
<Token>/BENM//ID/Payment Code 7</Token>
</Group>
</Data>
But, my generated output is like this:
<Data>
<Group>
<Token>/CHG1/</Token>
<Token>/EXCH/</Token>
<Token>/CPRP/</Token>
</Group>
<Group>
<Token>/CHG3/</Token>
<Token>/PURP//CD/</Token>
</Group>
<Group>
<Token>/CHG5/</Token>
<Token>/PURP//PRTRY/</Token>
</Group>
<Group>
<Token>/ORIG//CSID/</Token>
<Token>/BENM//ID/</Token>
</Group>
</Data>
Here is my XSLT that I've been using:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()[boolean(normalize-space())]|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<xsl:for-each select=".">
<Group>
<xsl:analyze-string select="." regex="\/[A-Z]+[0-9]?\/(\/(CD|PRTRY|MARF|ID|CSID|NAME|RID)\/)?">
<xsl:matching-substring>
<xsl:variable name="val" select="."/>
<Token>
<xsl:value-of select="$val"/>
</Token>
</xsl:matching-substring>
</xsl:analyze-string>
</Group>
</xsl:for-each>
</xsl:template>
There is something missing in my regex expression. Can anyone help me figure out? Thank you for your feedback.
I can't make heads or tails of your expected output. I suspect the correct output should be actually something like:
<?xml version="1.0" encoding="UTF-8"?>
<Rec>
<Group>
<Token>CHG1/EUR1000,00</Token>
<Token>EXCH/0,10</Token>
<Token>CPRP/Payment Code 1</Token>
</Group>
<Group>
<Token>CHG1/EUR1000,00</Token>
<Token>EXCH/0,10</Token>
<Token>CPRP/Payment Code 1</Token>
</Group>
<Group>
<Token>CHG3/EUR3000,00</Token>
<Token>PURP/</Token>
<Token>CD/Payment Code 3</Token>
</Group>
<Group>
<Token>CHG5/EUR5000,00</Token>
<Token>PURP/</Token>
<Token>PRTRY/Payment Code 5</Token>
</Group>
<Group>
<Token>ORIG/</Token>
<Token>CSID/EUR7000,00</Token>
<Token>BENM/</Token>
<Token>ID/Payment Code 7</Token>
</Group>
</Rec>
If so, I would suggest you change your approach and try:
XSLT 2.0
<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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<xsl:variable name="t" select="tokenize(., '/')" />
<Group>
<xsl:for-each select="$t[position() mod 2 = 0]">
<xsl:variable name="i" select="index-of($t, .)"/>
<Token>
<xsl:value-of select="." />
<xsl:text>/</xsl:text>
<xsl:value-of select="$t[$i + 1]" />
</Token>
</xsl:for-each>
</Group>
</xsl:template>
</xsl:stylesheet>
Demo: http://xsltransform.net/93dEHGr
Or even simpler:
XSLT 2.0
<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="*"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Data">
<Group>
<xsl:for-each-group select="tokenize(., '/')" group-by="position() idiv 2">
<Token>
<xsl:value-of select="current-group()" separator="/"/>
</Token>
</xsl:for-each-group>
</Group>
</xsl:template>
</xsl:stylesheet>
Demo: http://xsltransform.net/93dEHGr/2
Added:
Actually, the keys with 2 '//' in the middle is considered as 1 key.
Well, then let's take the // strings out of the game before doing the tokenizing, and restore them at the end:
<xsl:template match="Data">
<Group>
<xsl:for-each-group select="tokenize(replace(., '//', '§§§'), '/')" group-by="position() idiv 2">
<Token>
<xsl:value-of select="replace(string-join(current-group(), '/'), '§§§', '//')" />
</Token>
</xsl:for-each-group>
</Group>
</xsl:template>
Result
<?xml version="1.0" encoding="UTF-8"?>
<Rec>
<Group>
<Token/>
<Token>CHG1/EUR1000,00</Token>
<Token>EXCH/0,10</Token>
<Token>CPRP/Payment Code 1</Token>
</Group>
<Group>
<Token/>
<Token>CHG1/EUR1000,00</Token>
<Token>EXCH/0,10</Token>
<Token>CPRP/Payment Code 1</Token>
</Group>
<Group>
<Token/>
<Token>CHG3/EUR3000,00</Token>
<Token>PURP//CD/Payment Code 3</Token>
</Group>
<Group>
<Token/>
<Token>CHG5/EUR5000,00</Token>
<Token>PURP//PRTRY/Payment Code 5</Token>
</Group>
<Group>
<Token/>
<Token>ORIG//CSID/EUR7000,00</Token>
<Token>BENM//ID/Payment Code 7</Token>
</Group>
</Rec>
http://xsltransform.net/93dEHGr/4
You can try this with tokenize in xslt 2.0
<xsl:template match="Rec">
<Data>
<xsl:apply-templates/>
</Data>
</xsl:template>
<xsl:template match="Data">
<Group>
<xsl:for-each select="tokenize(., '0/')">
<Token>
<xsl:if test="position() ne 1">
<xsl:text>/</xsl:text>
</xsl:if>
<xsl:value-of select="."/>
<xsl:if test="position() ne last()">
<xsl:text>0</xsl:text>
</xsl:if>
</Token>
</xsl:for-each>
</Group>
</xsl:template>
I am trying to re-structure xml data into groups and sub-group. I was able to get it to work, but my code has to include something, that looks (at least to me) like a workaround. Here are my sample files:
Data.xml:
<data>
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g2" SubGroup="sg1">Record 2</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
<record Group="g2" SubGroup="sg1">Record 4</record>
<record Group="g2" SubGroup="sg2">Record 5</record>
<record Group="g1" SubGroup="sg2">Record 6</record>
</data>
Stylesheet.xsl:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/>
<xsl:key name="Group" match="record" use="#Group" />
<xsl:key name="SubGroup" match="record" use="#SubGroup" />
<xsl:template match="/data">
<xsl:variable name="Records" select="record"/>
<data>
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('Group',#Group)[1])]">
<xsl:sort select="#Group"/>
<xsl:variable name="Group" select="#Group"/>
<xsl:call-template name="Group">
<xsl:with-param name="Records" select="$Records[#Group = $Group]"/>
<xsl:with-param name="Group" select="$Group"/>
</xsl:call-template>
</xsl:for-each>
</data>
</xsl:template>
<xsl:template name="Group">
<xsl:param name="Records"/>
<xsl:param name="Group"/>
<group name="{$Group}">
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',#SubGroup)[1])]">
<!-- this works: <xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',#SubGroup)[#Group = $Group][1])]"> -->
<xsl:sort select="#SubGroup"/>
<xsl:variable name="SubGroup" select="#SubGroup"/>
<xsl:call-template name="SubGroup">
<xsl:with-param name="Records" select="$Records[#SubGroup = $SubGroup]"/>
<xsl:with-param name="Group" select="$Group"/>
<xsl:with-param name="SubGroup" select="$SubGroup"/>
</xsl:call-template>
</xsl:for-each>
</group>
</xsl:template>
<xsl:template name="SubGroup">
<xsl:param name="Records"/>
<xsl:param name="Group"/>
<xsl:param name="SubGroup"/>
<subgroup name="{$SubGroup}">
<xsl:for-each select="$Records">
<xsl:copy-of select="."/>
</xsl:for-each>
</subgroup>
</xsl:template>
</xsl:stylesheet>
This is the output generated:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<group name="g1">
<subgroup name="sg1">
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
</subgroup>
</group>
<group name="g2">
<subgroup name="sg2">
<record Group="g2" SubGroup="sg2">Record 5</record>
</subgroup>
</group>
</data>
but this is the output, I want to have:
<?xml version="1.0" encoding="UTF-8"?>
<data>
<group name="g1">
<subgroup name="sg1">
<record Group="g1" SubGroup="sg1">Record 1</record>
<record Group="g1" SubGroup="sg1">Record 3</record>
</subgroup>
<subgroup name="sg2">
<record Group="g1" SubGroup="sg2">Record 6</record>
</subgroup>
</group>
<group name="g2">
<subgroup name="sg1">
<record Group="g2" SubGroup="sg1">Record 2</record>
<record Group="g2" SubGroup="sg1">Record 4</record>
</subgroup>
<subgroup name="sg2">
<record Group="g2" SubGroup="sg2">Record 5</record>
</subgroup>
</group>
</data>
The problem is the for-each loop in the tempalte named "Group". It seems, that the key()-function is not working on the nodes contained in $Records but on the entire input XML file.
I get identical results with xsltproc and with saxon, so I do not think, it is a bug in my xslt processor. It seems, that I did not completely understand, how key() works.
If I add an additional selector [#Group = $Group] to key()'s output, I get the expected result.
Can somebody explain what is going on and why the additional selector [#Group = $Group] is needed.
Mario
When you want to do sub-grouping, you need to use a concatenated key of both the main group and the sub-group
<xsl:key name="SubGroup" match="record" use="concat(#Group,'|', #SubGroup)" />
Then, just use it in the same way as before, with the concatenation
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(#Group,'|', #SubGroup))[1])]">
Try this XSLT (which I have also simplified to utilise the key when calling your named templates with the records)
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" version="1.0" indent="yes" encoding="UTF-8"/>
<xsl:key name="Group" match="record" use="#Group" />
<xsl:key name="SubGroup" match="record" use="concat(#Group,'|', #SubGroup)" />
<xsl:template match="/data">
<data>
<xsl:for-each select="record[generate-id(.)=generate-id(key('Group',#Group)[1])]">
<xsl:sort select="#Group"/>
<xsl:call-template name="Group">
<xsl:with-param name="Records" select="key('Group',#Group)"/>
</xsl:call-template>
</xsl:for-each>
</data>
</xsl:template>
<xsl:template name="Group">
<xsl:param name="Records"/>
<group name="{#Group}">
<xsl:for-each select="$Records[generate-id(.)=generate-id(key('SubGroup',concat(#Group,'|', #SubGroup))[1])]">
<xsl:sort select="#SubGroup"/>
<xsl:call-template name="SubGroup">
<xsl:with-param name="Records" select="key('SubGroup',concat(#Group,'|', #SubGroup))"/>
</xsl:call-template>
</xsl:for-each>
</group>
</xsl:template>
<xsl:template name="SubGroup">
<xsl:param name="Records"/>
<subgroup name="{#SubGroup}">
<xsl:for-each select="$Records">
<xsl:copy-of select="."/>
</xsl:for-each>
</subgroup>
</xsl:template>
</xsl:stylesheet>
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>
I want to transfer a non-xml text file delimited by '|' characters into an xml using Datapower.
Following is file (sample1)
10|20003|24/23/25|23890
Now i have to break this into the following XML
<ResponseType>
<ResCode>10</ResCode>
<Id>20003</Id>
<SoftCode>24/23/25</SoftCode>
<StatusCode>23890</StatusCode>
</ResponseType>
What I did was following--
1>Create a Transform action in the service that will be receiving non-XML requests.
2>Select "Use XSLT specified in this action on a non-XML message" to specify that this is a Binary Transform.
3>Upload the following stylesheet as the Processing Control File.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
version="1.0">
<dp:input-mapping href="sample1.ffd" type="ffd"/>
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:copy-of select="ResponseType"/>
<xsl:call-template name="str:tokenize">
<xsl:with-param name="string" select="string" />
</xsl:call-template>
</xsl:template>
<xsl:template name="str:tokenize">
<xsl:with-param name="string" select="">
str:tokenize('string', '|')
</xsl:with param>
</xsl:template>
</xsl:stylesheet>
and here is my sample1.ffd(which I have uploaded in my local:// directory in Datapower
<File name="ResponseType">
<!-- capture all data into this tag -->
<Field name="ResCode/Id/SoftCode/StatusCode" />
</File>
But I am not getting desired output , I think my xslt is quite wrong
What can I do do to get desired output?
In DataPower using FFD the following should work:
1) Add the FFD file (below one of my old education samples):
<File name="CSVFILE">
<Group name="CSVLine" minOccurs="0" maxOccurs="unbounded" delim="\n">
<Field name="id"/>
<Field name="fname" delim=","/>
<Field name="lname" delim=","/>
<Field name="title" delim=","/>
<Field name="dept" delim=","/>
<Field name="org"/>
</Group>
</File>
2) Add the XSLT (this one simply copies the FFD transformed XML to output):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:dp="http://www.datapower.com/extensions"
version="1.0">
<dp:input-mapping href="CSVFILE.FFD" type="ffd"/>
<!-- This stylesheet copies the input to the output -->
<xsl:output method="xml"/>
<xsl:template match="/">
<xsl:copy-of select="." />
</xsl:template>
</xsl:stylesheet>
3) Send a message:
1,Anders,Wasen,B2B Architect,DataPower Dev,Enfo Zystems
2,Jean-Luc,Piccard,Captain,USS Enterprise,Star Fleet
4) This will result in the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<CSVFILE>
<CSVLine>
<id>1</id>
<fname>Anders</fname>
<lname>Wasen</lname>
<title>B2B Architect</title>
<dept>DataPower Dev,Enfo Zystems</dept>
<org/>
</CSVLine>
<CSVLine>
<id>2</id>
<fname>Jean-Luc</fname>
<lname>Piccard</lname>
<title>Captain</title>
<dept>USS Enterprise,Star Fleet</dept>
<org/>
</CSVLine>
</CSVFILE>
Make sure that you change the XSLT Transform action into "Transform Binary" and set Request Type to "non-xml", else it will not work!
Hope this will help you! :)
I'm not sure how IBM Datapower might solve this problem, but for XSLT, you would at least wrap your input in a XML element:
<Whatever>
10|20003|24/23/25|23890
</Whatever>
And then you could go on with a transformation like follows. The hard part is splitting your text input. In XSLT 1.0, there is no function available for that, so you need a recursive template.
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxml="urn:schemas-microsoft-com:xslt" version="1.0" exclude-result-prefixes="msxml">
<xsl:output method="xml" indent="yes" />
<xsl:template match="/">
<xsl:variable name="tokenized">
<items>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="//text()" />
</xsl:call-template>
</items>
</xsl:variable>
<ResponseType>
<ResCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[1]/text()" />
</ResCode>
<Id>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[2]/text()" />
</Id>
<SoftCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[3]/text()" />
</SoftCode>
<StatusCode>
<xsl:copy-of select="msxml:node-set($tokenized)/items/item[4]/text()" />
</StatusCode>
</ResponseType>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="string" />
<xsl:variable name="item" select="normalize-space( substring-before( concat( $string, '|'), '|'))" />
<xsl:if test="$item">
<item>
<xsl:value-of select="$item" />
</item>
<xsl:call-template name="tokenize">
<xsl:with-param name="string" select="substring-after($string,'|')" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Say I have the following XML:
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1"/>
<references tokID="t2"/>
</group>
<group gID="s2">
<references tokID="t3"/>
</group>
</relatedStuff>
</root>
Now, considering that a for-each loop for every token would be pretty inefficient and a bad idea, how would one go about using template matching, to transform this xml into the following?
<s id="everything_merged">
<tok id="t1" gID="s1" >blah</tok>
<tok id="t2" gID="s1" >blabla</tok>
<tok id="t3" gID="s2" >shovel</tok>
</s>
All I want from <s> is the "gID", the gID corresponding to the token in the <tokens>.
<xsl:for-each select="b:root/a:tokens/a:token">
<!-- and here some template matching -->
<xsl:attribute name="gID">
<xsl:value-of select="--correspondingNode's--#gID"/>
</xsl:attribute>
</xsl:for-each>
I'm pretty fuzzy on this sort of thing, so thank you very much for any help!
The following stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<s id="everything_merged">
<xsl:apply-templates select="/root/tokens/token" />
</s>
</xsl:template>
<xsl:template match="token">
<tok id="{#ID}" gID="{/root/relatedStuff/group[
references[#tokID=current()/#ID]]/#gID}">
<xsl:apply-templates />
</tok>
</xsl:template>
</xsl:stylesheet>
Applied to this input (corrected for well-formedness):
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1" />
<references tokID="t2" />
</group>
<group gID="s2">
<references tokID="t3" />
</group>
</relatedStuff>
</root>
Produces:
<s id="everything_merged">
<tok id="t1" gID="s1">blah</tok>
<tok id="t2" gID="s1">blabla</tok>
<tok id="t3" gID="s2">shovel</tok>
</s>
A solution using keys and pure "push-style:
<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:key name="kgIDfromTokId" match="#gID"
use="../*/#tokID"/>
<xsl:template match="tokens">
<s id="everything_merged">
<xsl:apply-templates/>
</s>
</xsl:template>
<xsl:template match="token">
<tok id="{#ID}" gID="{key('kgIDfromTokId', #ID)}">
<xsl:apply-templates/>
</tok>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<root>
<tokens>
<token ID="t1">blah</token>
<token ID="t2">blabla</token>
<token ID="t3">shovel</token>
</tokens>
<relatedStuff>
<group gID="s1">
<references tokID="t1" />
<references tokID="t2" />
</group>
<group gID="s2">
<references tokID="t3" />
</group>
</relatedStuff>
</root>
the wanted, correct result is produced:
<s id="everything_merged">
<tok id="t1" gID="s1">blah</tok>
<tok id="t2" gID="s1">blabla</tok>
<tok id="t3" gID="s2">shovel</tok>
</s>