Not able to loop through two child nodes simultaneously [closed] - xslt

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 6 years ago.
Improve this question
input xml:
Required output format:
Using for-each we can traverse through all child tags under either "headers" or "objects"; but how to traverse through both tags and select values and combine them as shown above(just one weird example :)). By using xsl transformation. Any kind of help will be greatly appreciated...

Input (please post only text. easier to copy)
<payload>
<headers>
<header>
<name>entry1</name>
</header>
<header>
<name>entry2</name>
</header>
<header>
<name>entry3</name>
</header>
<header>
<name>entry4</name>
</header>
</headers>
<objects>
<row>
<value>1231</value>
</row>
<row>
<value>342</value>
</row>
<row>
<value>98789</value>
</row>
<row>
<value>6576</value>
</row>
</objects>
</payload>
XSLT: (there are many solutions)
<?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="/">
<xsl:element name="objects">
<xsl:for-each select="payload/objects/row">
<xsl:variable name="i" select="position()"/>
<entry name="{/payload/headers/header[$i]/name}" value="{value}"></entry>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Result
<objects>
<entry name="entry1" value="1231"></entry>
<entry name="entry2" value="342"></entry>
<entry name="entry3" value="98789"></entry>
<entry name="entry4" value="6576"></entry>
</objects>

Related

How to group nearby same elements using XSLT version 1 [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
I need to transform an XML structure to another XML structure by grouping nearby elements using XSLT version 1 and store into a variable for later process.
My current solution:
https://xsltfiddle.liberty-development.net/naZXVF1/3
XML
<?xml version="1.0"?>
<Items>
<Parameter>1</Parameter>
<Parameter>2</Parameter>
<Menu>1</Menu>
<Parameter>3</Parameter>
<Parameter>4</Parameter>
<Menu>2</Menu>
<Menu>3</Menu>
<Parameter>5</Parameter>
<Parameter>6</Parameter>
<Parameter>7</Parameter>
<Parameter>8</Parameter>
</Items>
Expected Result
<Items>
<Parameters>
<Parameter>1</Parameter>
<Parameter>2</Parameter>
</Parameters>
<Menus>
<Menu>1</Menu>
</Menus>
...
</Items>
Either use sibling recursion or use a complicated key to identify the elements belonging together:
<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output indent="yes"/>
<xsl:key name="group" match="*[name(preceding-sibling::*[1]) = name()]"
use="generate-id(preceding-sibling::*[name() = name(current())][name(preceding-sibling::*[1]) != name()][1])"/>
<xsl:template match="Items">
<xsl:copy>
<xsl:apply-templates select="*[name(preceding-sibling::*[1]) != name()]"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Items/*">
<xsl:element name="{name()}s">
<xsl:copy-of
select=". | key('group', generate-id())"/>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/naZXVF1/4

Transforming attribute values into xml element

Using xslt 1.0, I need to transform below input xml to output xml
--input xml
<Row>
<Column name="NUMBER" sqltype="int">123</Column>
<Column name="DEPT1" sqltype="int">A</Column>
<Column name="CUST_EMPTYPE" sqltype="int">1</Column>
<Column name="CUST_TIJD" sqltype="int">31</Column>
</Row>
--output xml
<EMPLOYEE xmlns="http://xmlns.oracle.com/Employee">
<NUMBER>123</NUMBER>
<DEPT1>IHC</DEPT1>
<CUST_EMPTYPE>1</LASTNAME>
<CUST_TIJD>31</FIRSTNAME>
</EMPLOYEE>
the Column names from input xml are not known at design time, the Column can grow..
Can anyone let me know how to achieve this?
Thank you very much,
Yes. It is quite simple. One minor difference is the deviation between your DEPT1 source and destination value (I really don't know where 'IHC' may have been coming from). I harmonized it in the following XSLT code which just sets the Column/#name nodes to new EMPLOYEE elements with the content of the old text() content:
<?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" />
<xsl:template match="/Row">
<EMPLOYEE xmlns="http:xmlns.oracle.com/Employee">
<xsl:for-each select="Column">
<xsl:element name="{#name}">
<xsl:value-of select="text()" />
</xsl:element>
</xsl:for-each>
</EMPLOYEE>
</xsl:template>
</xsl:stylesheet>
The result of this is:
<EMPLOYEE xmlns="http:xmlns.oracle.com/Employee">
<NUMBER>123</NUMBER>
<DEPT1>A</DEPT1>
<CUST_EMPTYPE>1</CUST_EMPTYPE>
<CUST_TIJD>31</CUST_TIJD>
</EMPLOYEE>

Change template so XSLT Outputs a sum instead of a list of values

I have an XSLT template that is working fine.
<xsl:template match="Row[contains(BenefitType, 'MyBenefit')]">
<value>
<xsl:value-of select="BenefitList/Row/Premium* 12" />
</value>
</xsl:template>
The output is
<value>100</value>
<value>110</value>
What I would prefer is if it would just output 220. So, basically in the template I would need to use some sort of variable or looping to do this and then output the final summed value?
XSLT 1 compliance is required.
The template is being used as follows:
<xsl:apply-templates select="Root/Row[contains(BenefitType, 'MyBenefit')]" />
For some reason, when I use the contains here it only sums the first structure that matches and not all of them. If The XML values parent wasn't dependent on having a sibling element that matched a specific value then a'sum' approach would work.
The direct solution to the problem was already mentioned in the comments, but assuming you really want to do the same with some variables, this might be interesting for you:
XML:
<Root>
<Row>
<BenefitType>MyBenefit</BenefitType>
<BenefitList>
<Premium>100</Premium>
</BenefitList>
</Row>
<Row>
<BenefitType>MyBenefit, OtherBenefit</BenefitType>
<BenefitList>
<Premium>100</Premium>
</BenefitList>
</Row>
<Row>
<BenefitType>OtherBenefit</BenefitType>
<BenefitList>
<Premium>1000</Premium>
</BenefitList>
</Row>
<Row>
<BenefitType>OtherBenefit</BenefitType>
<BenefitList>
<Premium>1000</Premium>
</BenefitList>
</Row>
</Root>
XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl">
<xsl:template match="/">
<total>
<xsl:variable name="valuesXml">
<values>
<xsl:apply-templates select="Root/Row[contains(BenefitType, 'MyBenefit')]" />
</values>
</xsl:variable>
<xsl:variable name="values" select="exsl:node-set($valuesXml)/values/value" />
<xsl:value-of select="sum($values)" />
</total>
</xsl:template>
<xsl:template match="Row[contains(BenefitType, 'MyBenefit')]">
<value>
<xsl:value-of select="BenefitList/Premium * 12" />
</value>
</xsl:template>
</xsl:stylesheet>
Here the same result set generated in your question is saved in another variable, which can then again be processed.

Compare 2 sets of child nodes to find a match where a least one of the child node values matches one of the other child node values

I have this XML with two sets of table data (element names generalized for simplicity).
<root>
<table>
<Row type="1">
<Id>AAAA</Id>
<Properties>
<Property>A</Property>
<Property>D</Property>
</Properties>
</Row>
<Row type="1">
<Id>BBBB</Id>
<Properties>
<Property>B</Property>
</Properties>
</Row>
<Row type="1">
<Id>CCCC</Id>
<Properties>
<Property>G</Property>
<Property>H</Property>
</Properties>
</Row>
</table>
<table>
<Row type="2">
<Id>123abc</Id>
<Properties>
<Property>A</Property>
<Property>D</Property>
<Property>E</Property>
</Properties>
</Row>
<Row type="2">
<Id>456def</Id>
<Properties>
<Property>B</Property>
<Property>C</Property>
<Property>I</Property>
</Properties>
</Row>
<Row type="2">
<Id>798ghi</Id>
<Properties>
<Property>F</Property>
<Property>G</Property>
<Property>H</Property>
</Properties>
</Row>
</table>
</root>
I am trying to write a transform to output a new table that associates a row from table 1 to a row in table 2 based on their properties. Rows in table 1 are not required to have all of the properties present in a row in table 2; any one property is all that's required to be considered a match. There will always only be a one to one relationship between a row in table 1 and a row in table 2.
My desired output is:
<root>
<Row>
<Name>NewTable:AAAA</Name>
<Table2Id>123abc</Table2Id>
</Row>
<Row>
<Name>NewTable:BBBB</Name>
<Table2Id>456def</Table2Id>>
</Row>
<Row>
<Name>NewTable:CCCC</Name>
<Table2Id>789ghi</Table2Id>
</Row>
</root>
I've started with this and have been trying to follow this logic: Find me the Id tag of the row in table 2 who has at least one property that matches one of the properties of the current row being processed in table 1.
Here's what I have so far. It's not working, but I feel like I'm close.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="//Row[#type != '2']">
<xsl:variable name="Name" select="concat('NewTable:', ./Id)"/>
<Row>
<Name>
<xsl:value-of select="$Name"/>
</Name>
<Table2Id>
<xsl:value-of select="//Row[#type = '2'][Property = ./Property]/Id"/>
</Table2Id>
</Row>
</xsl:template>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
There are two things that can be very useful here: one is the key feature of XSLT that allows you to create a relationship based on matching values. The other is set comparison, where if at least one member of a set matches at least one member of another set, the sets will be considered matching.
Try the following stylesheet that takes advantage of both:
<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:key name="table2" match="Row[#type=2]" use="Properties/Property" />
<xsl:template match="/">
<root>
<xsl:for-each select="root/table/Row[#type=1]">
<row>
<Name>
<xsl:value-of select="Id" />
</Name>
<Table2Id>
<xsl:value-of select="key('table2', Properties/Property)/Id" />
</Table2Id>
</row>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
BTW, your method would have worked too (albeit less efficiently) if only you had used:
[Properties/Property = ./Properties/Property]
instead of just:
[Property = ./Property]
since you are in the context of a <Row> (a grandparent of <Property>).

namespaces not giving expected results

I am trying to read the following XML using XSLT, but can not get the expected results.
If i remove the "xmlns:a="http://schemas.datacontract.org/2004/07/CoreModels" namespace from the txnDetail node, then the xslt below works fine ???
What am i doing wrong ?
The input XML:
<TransactionRsp xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<avlBal>848.35</avlBal>
<blkAmt>0</blkAmt>
<txnDetail xmlns:a="http://schemas.datacontract.org/2004/07/CoreModels">
<a:txnDetail>
<a:billAmount>400</a:billAmount>
<a:txnDateTime>2012-02-23T14:35:45</a:txnDateTime>
</a:txnDetail>
<a:txnDetail>
<a:billAmount>10</a:billAmount>
<a:txnDateTime>2012-07-30T12:22:14</a:txnDateTime>
</a:txnDetail>
</txnDetail>
</TransactionRsp>
The XSLT stylesheet:
<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="/">
<root>
<xsl:for-each select="TransactionRsp/txnDetail/txnDetail">
<row>
<col name="billAmount"><xsl:value-of select="billAmount"/></col>
<col name="itemID"><xsl:value-of select="itemID"/></col>
</row>
</xsl:for-each>
</root>
</xsl:template>
</xsl:stylesheet>
You need to qualify your XPath expressions with namespace prefix:
<xsl:for-each select="TransactionRsp/txnDetail/a:txnDetail">
<row>
<col name="billAmount"><xsl:value-of select="a:billAmount"/></col>
<col name="itemID"><xsl:value-of select="a:itemID"/></col>
</row>
</xsl:for-each>
EDIT: The namespace must be declared for your XSLT file too. Thanks to #hcayless's comments below.
<xsl:stylesheet version = '1.0'
xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
xmlns:a="http://schemas.datacontract.org/2004/07/CoreModels" >