Create xsl key by "joining" elements - xslt

<t>
<rendition xml:id="b">color: blue</rendition>
<rendition xml:id="r">color: red</rendition>
<tagUsage gi="p" render="b" />
<tagUsage gi="emph" render="r" />
</t>
How would I create an XSL 1.0 key into rendition elements based on #gi in the tagUsage element, joining rendition/#xml:id to tagUsage/#render? Something like
<xsl:key name="rendition-by-tagName"
match="rendition"
use="//tagUsage[#xml:id of rendition = #render of tagUsage]/#gi" />
so that given "p", the key would return the blue rendition; given "emph", the key would return the red rendition.

Use:
<xsl:key name="kRendByUsageGi" match="rendition"
use="../tagUsage[#render=current()/#xml:id]/#gi"/>
Here is a complete verification:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kRendByUsageGi" match="rendition"
use="../tagUsage[#render=current()/#xml:id]/#gi"/>
<xsl:template match="/">
<xsl:copy-of select="key('kRendByUsageGi', 'p')/text()"/>
========
<xsl:copy-of select="key('kRendByUsageGi', 'emph')/text()"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<t>
<rendition xml:id="b">color: blue</rendition>
<rendition xml:id="r">color: red</rendition>
<tagUsage gi="p" render="b" />
<tagUsage gi="emph" render="r" />
</t>
the wanted, correct result is produced:
color: blue
========
color: red

I found that the following, which uses a second key(), works with xsltproc, so, if that's your target processor, this should help. It doesn't work with Saxon, though.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name='kTagUsage' match='tagUsage' use='#render'/>
<xsl:key name="kRendByUsageGi" match="rendition"
use="key('kTagUsage', #xml:id)/#gi"/>
<xsl:template match="/">
<xsl:copy-of select="key('kRendByUsageGi', 'p')/text()"/>
========
<xsl:copy-of select="key('kRendByUsageGi', 'emph')/text()"/>
</xsl:template>
</xsl:stylesheet>

Related

XSLT – Compare node values and pass attributes if nodes are equal

I am trying to do something that is probably very simple, but my very rudimentary xslt is not up to it.
Given the following XML:
<?xml version="1.0" encoding="UTF-8"?>
<MyLists>
<List1>
<Place01 ctr="PTG">Lisbon</Place01>
<Place02 ctr="SPA">Madrid</Place02>
<Place03 ctr="FRA">Paris</Place03>
<Place04 ctr="ENG">York</Place04>
</List1>
<List2>
<Item01 type="country">Italy</Item01>
<Item02 type="person">John</Item02>
<Item03 type="city">York</Item03>
<Item04 type="city" subtype="capital">Madrid</Item04>
</List2>
</MyLists>
I would like to compare the text nodes from <List1> and <List2>, and, whenever their values are the same, pass, for each element, the attributes from <List2> to the corresponding items in <List1>, in order to get:
<?xml version="1.0" encoding="UTF-8"?>
<MyLists>
<List1>
<Place01 ctr="PTG">Lisbon</Place01>
<Place02 ctr="SPA" type="city" subtype="capital">Madrid</Place02>
<Place03 ctr="FRA">Paris</Place03>
<Place04 ctr="ENG" type="city">York</Place04>
</List1>
<List2>
<Item01 type="country">Italy</Item01>
<Item02 type="person">John</Item02>
<Item03 type="city">York</Item03>
<Item04 type="city" subtype="capital">Madrid</Item04>
</List2>
</MyLists>
Ideally, I'd like to be able to copy whichever attributes these element possess, without having to specify them.
Many thanks in advance!
If I understand this correctly, you could do something like:
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="*"/>
<xsl:key name="match" match="Item" use="." />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Place">
<xsl:copy>
<xsl:copy-of select="key('match', .)/#*"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>

Checking multiple attribute values in multiple nodes with XSL

the xml is as follows.
<MyXml>
<Machine1>
<SupportedOS>
<OS1 MajorVersion=1 MinorVersion=2/>
<OS2 MajorVersion=2 MinorVersion=0/>
<OS3 MajorVersion=1 MinorVersion=1/>
<OS4 MajorVersion=2 MinorVersion=1/>
<OS5 MajorVersion=3 MinorVersion=0/>
</SupportedOS>
</Machine1>
</MyXml>
I get the OS version from some source (say MajorVersion=x1 and MinorVersion=x2) inside this XML and Add both the xml programitically.
The task is to compare the Both the Minor and Major Version of OS and olny if the both of them are same to the source only then copy the Machine node to the transformed xml.
Let me know the ways to do it. I will try to code myself.
EDIT
I want to copy the Machine Node when any one of the OS node has MajorVersion=x1 and MinorVersion=x2.
As simple as this:
<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:param name="pMajor" select="1"/>
<xsl:param name="pMinor" select="2"/>
<xsl:template match="/*/*">
<xsl:copy-of select=
"self::*[*/*[#MajorVersion = $pMajor and #MinorVersion = $pMinor]]"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the following XML document (the provided one is severely malformed and not useful!):
<MyXml>
<Machine1>
<SupportedOS>
<OS1 MajorVersion="1" MinorVersion="2"/>
<OS2 MajorVersion="2" MinorVersion="0"/>
<OS3 MajorVersion="1" MinorVersion="1"/>
<OS4 MajorVersion="2" MinorVersion="1"/>
<OS5 MajorVersion="3" MinorVersion="0"/>
</SupportedOS>
</Machine1>
<Machine2>
<SupportedOS>
<OS1 MajorVersion="1" MinorVersion="3"/>
<OS2 MajorVersion="2" MinorVersion="0"/>
<OS3 MajorVersion="1" MinorVersion="1"/>
<OS4 MajorVersion="2" MinorVersion="1"/>
<OS5 MajorVersion="3" MinorVersion="0"/>
</SupportedOS>
</Machine2>
</MyXml>
the wanted, correct result is produced:
<Machine1>
<SupportedOS>
<OS1 MajorVersion="1" MinorVersion="2"/>
<OS2 MajorVersion="2" MinorVersion="0"/>
<OS3 MajorVersion="1" MinorVersion="1"/>
<OS4 MajorVersion="2" MinorVersion="1"/>
<OS5 MajorVersion="3" MinorVersion="0"/>
</SupportedOS>
</Machine1>
Compare attributes if they aren't satisfying the condition .. if yes then drop them..
Or else copy them
in the below code, first template copies all nodes,
second template drops OS that is having MajorVersion not equal to 'x1' and MinorVersion not equal to 'x2'
<?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" indent="yes"/>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/MyXml/Machine1/SupportedOS/OS1[#MajorVersion != 'x1' and #MajorVersion!='x2']"/>
</xsl:stylesheet>
<?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"/>
<xsl:template match="/MyXml">
<xsl:apply-templates select="//SupportedOS"/>
</xsl:template>
<xsl:template match="SupportedOS">
<xsl:for-each select="child::*">
<xsl:if test="#MajorVersion='1' and #MinorVersion='2'">
<xsl:copy-of select="//Machine1"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>

change of xml structure using xslt

I want to convert the following source xml structure to the target xml structure using xslt transformation. I am mot able to convert the following source xml to target xml using xslt. Please help us in coneverting this.
Source XML
<XxhrPiEmpcompOutIntCollection>
<XxhrPiEmpcompOutInt>
<employeeNumber>
200000562
</employeeNumber>
<competencyName>
Comp1
</competencyName>
<proficiencyLevel>
Prof1
</proficiencyLevel>
<compDateTo>
16-NOV-12
</compDateTo>
</XxhrPiEmpcompOutInt>
<XxhrPiEmpcompOutInt>
<employeeNumber>
200000562
</employeeNumber>
<competencyName>
Comp2
</competencyName>
<proficiencyLevel>
Prof2
</proficiencyLevel>
<compDateTo>
16-NOV-12
</compDateTo>
</XxhrPiEmpcompOutInt>
</XxhrPiEmpcompOutIntCollection>
Target xml
<EmployeeCompetencyRequest>
<EmployeeNumber>200000562</EmployeeNumber>
<Competencies>
<Competency>
<Name>Comp1</Name>
<ProficiencyLevel>Prof1</ProficiencyLevel>
<EndDate>16-NOV-12</EndDate>
</Competency>
<Competency>
<Name>Comp2</Name>
<ProficiencyLevel>Prof2</ProficiencyLevel>
<EndDate>16-NOV-12</EndDate>
</Competency>
</Competencies>
</<EmployeeCompetencyRequest>
Some hint :
<EmployeeCompetencyRequest>
template match /XxhrPiEmpcompOutIntCollection
for-each-group XxhrPiEmpcompOutInt group by employeeNumber
<EmployeeNumber> value-of current-group-key </EmployeeNumber>
<Competencies>
for-each current-group
<Competency>
value-of name
value-of proficiencyLevel
</competency>
/for-each
</Competencies>
/for-each-group
<EmployeeCompetencyRequest>
Hope you can finish it.
You can group users with for-each-group:
<?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="2.0">
<xsl:output indent="yes"/>
<xsl:template match="/">
<EmployeeCompetencyRequest>
<xsl:for-each-group select="//XxhrPiEmpcompOutInt" group-by="employeeNumber">
<Name><xsl:value-of select="current-grouping-key()"/></Name>
<Competencies>
<xsl:for-each select="current-group()">
<Competency>
<Name><xsl:value-of select="competencyName"/></Name>
<ProfiencyLevel><xsl:value-of select="profiencyLevel"/></ProfiencyLevel>
<EndDate><xsl:value-of select="compDateTo"/></EndDate>
</Competency>
</xsl:for-each>
</Competencies>
</xsl:for-each-group>
</EmployeeCompetencyRequest>
</xsl:template>
Salut,
For XSLT 1.0 you must group widht xsl:key:
<?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" indent="yes"/>
<xsl:key name="groups" match="/XxhrPiEmpcompOutIntCollection/XxhrPiEmpcompOutInt" use="employeeNumber" />
<xsl:template match="/">
<EmployeeCompetencyRequest>
<xsl:apply-templates select="//XxhrPiEmpcompOutInt[generate-id() = generate-id(key('groups', employeeNumber)[1])]"/>
</EmployeeCompetencyRequest>
</xsl:template>
<xsl:template match="XxhrPiEmpcompOutInt">
<Name><xsl:value-of select="employeeNumber"/></Name>
<Competencies>
<xsl:for-each select="key('groups', employeeNumber)">
<Competency>
<Name><xsl:value-of select="competencyName"/></Name>
<ProfiencyLevel><xsl:value-of select="proficiencyLevel"/></ProfiencyLevel>
<EndDate><xsl:value-of select="compDateTo"/></EndDate>
</Competency>
</xsl:for-each>
</Competencies>
</xsl:template>
</xsl:stylesheet>
I can't test it with Oracle ...

How to copy a certain node (with children) from a XML with XSLT in Biztalk specifying a custom namespace?

I need to copy a subnode from a XML to a certain node of a new XML in a Biztalk Map using XSLT.
Consider the following input XML:
<?xml version="1.0" encoding="UTF-8"?>
<ns0:root xmlns:ns0="http://not/useful/data/">
<ns0:notuseful>
<ns0:foo></ns0:foo>
<ns0:foo2></ns0:foo2>
<ns0:blabla></ns0:blabla>
</ns0:notuseful>
<ns0:data>
<ns1:usefulDataList xmlns:ns1="http://useful/data/">
<ns1:usefulData>
<ns1:usefulChild1></ns1:usefulChild1>
<ns1:usefulChild2></ns1:usefulChild2>
<ns1:usefulChild3></ns1:usefulChild3>
<ns1:usefulChild4></ns1:usefulChild4>
<ns1:usefulChild5></ns1:usefulChild5>
</ns1:usefulData>
</ns1:usefulDataList>
</ns0:data>
<ns0:root>
What I need is to extract the node called "usefulDataList", so I need to copy it in a new XML like this one:
<?xml version="1.0" encoding="UTF-8"?>
<ns2:root2 xmln:ns2="http://new/xml">
<ns2:blabla>
<ns2:stuff />
</ns2:blabla>
<ns2:data>
<ns2:usefulDataList>
<ns2:usefulData>
<ns2:usefulChild1></ns2:usefulChild1>
<ns2:usefulChild2></ns2:usefulChild2>
<ns2:usefulChild3></ns2:usefulChild3>
<ns2:usefulChild4></ns2:usefulChild4>
<ns2:usefulChild5></ns2:usefulChild5>
</ns2:usefulData>
</ns2:usefulDataList>
</ns2:data>
</ns2:root2>
This should be done inside a Biztalk Functoid, as you see namespaces from source and target are diferent.
I'm an absolute beginner with XSLT, and I've been doing some tests, but I've something wrong with my XSLT expressions:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="http://new/xml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:template name="testTemplate" match="//*[local-name() = 'usefulDataList ']">
<xsl:element name="ns0:usefulDataList " namespace="">
<xsl:apply-templates mode="copy-no-ns" select="usefulDataList"/>
</xsl:element>
</xsl:template>
<xsl:template mode="copy-no-ns" match="*">
<xsl:element name="{name(.)}" namespace="{namespace-uri(.)}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-no-ns"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
I'd appreciate any tip, with XSLT or Biztalk mapper. I don't like linking a huge amount of fields one by one if I can solve it with a XSLT expression.
Greetings.
Beware you had a space in *[local-name() = 'usefulDataList ']" so that would never match. this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ns2="http://new/xml">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" />
<xsl:strip-space elements="*"/>
<xsl:template match="/">
<ns2:root>
<ns2:blabla>
<ns2:stuff />
</ns2:blabla>
<ns2:data>
<xsl:apply-templates mode="copy-no-ns" select="//*[local-name() = 'usefulDataList']"/>
</ns2:data>
</ns2:root>
</xsl:template>
<xsl:template mode="copy-no-ns" match="*">
<xsl:element name="ns2:{local-name(.)}">
<xsl:copy-of select="#*"/>
<xsl:apply-templates mode="copy-no-ns"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

Generate XSLT to map source value structures to destination field depending on source content

I have a source list of xml in this format:
<metadata>
<metadatum>
<description>OnEnter</description>
<value>Hello World</id>
</metadatum>
<metadatum>
<description>OnLeave</description>
<value>Goodbye World</id>
</metadatum>
</metadata>
and a target structure like this:
<friendlyText>
<onEnter>[Content Here]</onEnter>
<onLeave>[Content Here]</onLeave>
</friendlyText>
Is it possible to create an XSLT that will map the 'value' field in the metadata hierarchy to the proper target node depending on the source 'description'?
I'm trying to get this done with Altova MapForce; it feels like there should be an interface to allow this, I'm just not finding it.
<?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" indent="yes"/>
<xsl:template match="metadata">
<friendlyText>
<xsl:apply-templates select="metadatum"/>
</friendlyText>
</xsl:template>
<xsl:template match="metadatum">
<xsl:element name="{description}">
<xsl:value-of select="value"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<?xml version="1.0" encoding="utf-8"?>
<friendlyText>
<OnEnter>Hello World</OnEnter>
<OnLeave>Goodbye World</OnLeave>
</friendlyText>
This transformation is a general solution that can work with any "target structure" that is in a separate XML document:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" exclude-result-prefixes="my">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="vUpper" select=
"'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
<xsl:variable name="vLower" select=
"'abcdefghijklmnopqrstuvwxyz'"/>
<my:target>
<friendlyText>
<onEnter>[Content Here]</onEnter>
<onLeave>[Content Here]</onLeave>
</friendlyText>
</my:target>
<xsl:variable name="vTarget" select="document('')/*/my:target/*"/>
<xsl:variable name="vMeta" select="/*/metadatum"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="$vTarget"/>
</xsl:template>
<xsl:template match="friendlyText/*/text()">
<xsl:value-of select=
"$vMeta[translate(description, $vLower, $vUpper)
=
translate(name(current()/..), $vLower, $vUpper)
]/value"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document (corrected to be made well-formed):
<metadata>
<metadatum>
<description>OnEnter</description>
<value>Hello World</value>
</metadatum>
<metadatum>
<description>OnLeave</description>
<value>Goodbye World</value>
</metadatum>
</metadata>
produces the wanted, correct result:
<friendlyText xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:my="my:my">
<onEnter>Hello World</onEnter>
<onLeave>Goodbye World</onLeave>
</friendlyText>
Do note: Only for convenience, the "target structure" is inline here. In a real world case it would be better to keep the "target structure" in a separate file and to load it using the document() function. Only the line:
<xsl:variable name="vTarget" select="document('')/*/my:target/*"/>
will need to be changed to:
<xsl:variable name="vTarget" select="document('someFileUrl')/*"/>