change of xml structure using xslt - 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 ...

Related

Using XSLT to output only the id and the value of the “wert” Element?

Given the XML code:
<?xml version="1.0" encoding="utf-8"?>
<autoverleih>
<kunden>
<kunde id="p1">
<name nachname="Mustermann" vorname="Mario"/>
<versicherung name="super1" kasko="true"/>
</kunde>
<kunde id="p4">
<name nachname="Gans" vorname="Maria"/>
<versicherung name="die-beste" kasko="false"/>
</kunde>
<kunde id="p54">
<name nachname="Gans" vorname="Gustav"/>
<versicherung name="super1" kasko="false"/>
</kunde>
</kunden>
<ausleihen>
<vertrag kunde="p1" wert="1521.07" datum="2014-07-02" bezahlt="true"/>
<vertrag kunde="p4" wert="397.96" datum="2014-07-12" bezahlt="false"/>
<vertrag kunde="p1" wert="51.23" datum="2014-06-13" bezahlt="true"/>
<vertrag kunde="p54" wert="127.12" datum="2014-08-01" bezahlt="false"/>
</ausleihen>
</autoverleih>
And the following XSLT:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform". version="2.0">
<xsl:template match=”autoverleih">
<xsl:value-of select="./kunden/kunde/#id”>
</xsl:template>
<xsl:template match=”vertrag”>
<xsl:value-of select=”../kunde”/>
<xsl:value-of select=”./wert”/>
</xsl:template>
</xsl:stylesheet>
I need to change this code so that for each kunde it only outputs its Id and the wert attribute.
If you want to reference vertrag from kunde I think you can use a key:
<?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="#all"
version="3.0">
<xsl:output method="text"/>
<xsl:key name="vertrag-des-kunden" match="vertrag" use="#kunde"/>
<xsl:template match="/">
<xsl:value-of
select="//kunde/#id/concat(., ': ', string-join(key('vertrag-des-kunden', .)/#wert, ', '))"
separator="
"/>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/a9HjZU

XSLT transform error

I have the following xml:
<RootNode xmlns="http://someurl/path/path/path">
<Child1>
<GrandChild1>Value</GrandChild1>
<!-- Lots more elements in here-->
</Child1>
</RootNode>
I have the following xslt:
<xsl:stylesheet version="1.0" xmlns="http://someurl/path/path/path" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="RootNode/Child1">
<NewNodeNameHere>
<xsl:value-of select="GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The problem: this is the my result:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1 />
</NewRootNode>
I am expecting to see:
<?xml version="1.0" encoding="utf-8"?>
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<NewNodeNameHere>Value</NewNodeNameHere>
<!-- Other new elements with values from the xml file -->
</NewChild1>
</NewRootNode>
I am missing of the information inside of NewChild1 that should be there.
I think my for-each select is correct, so the only thing I can think of is that there is a problem with the namespace in the Xml and the namespace in the xslt. Can anybody see what I'm doing wrong?
The problem is caused by the namespaces.
Since the xml defines xmlns="http://someurl/path/path/path", it is not in the default namespace anymore.
You can define that namespace with an name like xmlns:ns="http://someurl/path/path/path" in the xsl, and then use that name in the XPath expression.
The following works for me:
<xsl:stylesheet version="1.0" xmlns:ns="http://someurl/path/path/path" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:template match="/">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns:RootNode/ns:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode >
</xsl:template>
</xsl:stylesheet>
The stylesheet namespace should be http://www.w3.org/1999/XSL/Transform instead of http://someurl/path/path/path.
Also, since the input XML uses a namespace all your XPath expressions should be namespace-qualified:
<xsl:template match="/" xmlns:ns1="http://someurl/path/path/path">
<NewRootNode xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<NewChild1>
<xsl:for-each select="ns1:RootNode/ns1:Child1">
<NewNodeNameHere>
<xsl:value-of select="ns1:GrandChild1"/>
</NewNodeNameHere>
<!-- lots of value-of tags in here -->
</xsl:for-each>
</NewChild1>
</NewRootNode>
</xsl:template>

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')/*"/>

XSL: How to concatenate nodes with conditions?

I have the following code (eg):
<response>
<parameter>
<cottage>
<cot>
<res>
<hab desc="Lakeside">
<reg cod="OB" prr="600.84>
<lwz>TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120830#20120831#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120831#20120901#EU#3-0#</lwz>
I need to create a concatenated string that includes the whole of the first 'lwz' line and then the price (200.26, but it can be different in each line) for each corresponding line.
So the output, separating each line with | would be:
TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#|200.26|200.26
Thanks
This XSLT 1.0 transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="lwz[1]">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="lwz[position() >1]">
<xsl:value-of select=
"concat('
',
substring-before(substring-after(substring-after(substring-after(.,'#'),'#'),'#'),'#')
)
"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided text (converted to a well-formed XML document !!!):
<response>
<parameter>
<cottage>
<cot>
<res>
<hab desc="Lakeside">
<reg cod="OB" prr="600.84">
<lwz>TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120830#20120831#EU#3-0#</lwz>
<lwz>TR#2#AB#200.26#0#QB#OK#20120831#20120901#EU#3-0#</lwz>
</reg>
</hab>
</res>
</cot>
</cottage>
</parameter>
</response>
produces the wanted, correct result:
TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#
200.26
200.26
II XSLT 2.0 solution:
This transformation:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="lwz[1]">
<xsl:value-of select="."/>
</xsl:template>
<xsl:template match="lwz[position() >1]">
<xsl:value-of select=
"concat('
', tokenize(.,'#')[4])"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the above XML document, again produces the wanted, correct result. Note the use of the standard XPath 2.0 function tokenize():
TR#2#AB#200.26#0#QB#OK#20120829#20120830#EU#3-0#
200.26
200.26
You can use the XPath substring function to select substrings from your lwz node data. You don't really give much more detail about your problem, if you want a more detailed answer, perhaps provide the full XML document and your best-guess XSLT