I have the following xml tree:
<cashflows>
<interestFlows>
<interestPaymentPeriod>
<calculationPeriod>
<startDate>20230102</startDate>
<endDate>20230130</endDate>
</calculationPeriod>
</interestPaymentPeriod>
<interestPaymentPeriod>
<calculationPeriod>
<startDate>20230202</startDate>
<endDate>20230228</endDate>
</calculationPeriod>
</interestPaymentPeriod>
<interestPaymentPeriod>
<calculationPeriod>
<startDate>20230301</startDate>
<endDate>20230330</endDate>
</calculationPeriod>
</interestPaymentPeriod>
</interestFlows>
<capitalFlows>
<flow>
<flowSubType>AMO<flowSubType>
<date>20230116<date>
</flow>
<flow>
<flowSubType>AMO<flowSubType>
<date>20230116<date>
</flow>
<flow>
<flowSubType>INT<flowSubType>
<date>20230116<date>
</flow>
</capitalFlows>
</cashflows>
For each flow[flowSubType = 'AMO'] then divide the corresponding section of InterestpaymentPeriod/calculationPeriod into two blocks.
If the AMO date falls inside an interestpayment period then divide its corresponding calculation period into two block like:
[startDate ; AMO date] and [AMO date ; endDate].
The desired output would be:
<cashflows>
<interestFlows>
<interestPaymentPeriod>
<calculationPeriod>
<startDate>20230102</startDate>
<endDate>20230116</endDate>
</calculationPeriod>
<calculationPeriod>
<startDate>20230116</startDate>
<endDate>20230130</endDate>
</calculationPeriod>
</interestPaymentPeriod>
<interestPaymentPeriod>
<calculationPeriod>
<startDate>20230202</startDate>
<endDate>20230220</endDate>
</calculationPeriod>
<calculationPeriod>
<startDate>20230220</startDate>
<endDate>20230228</endDate>
</calculationPeriod>
</interestPaymentPeriod>
<interestPaymentPeriod>
<calculationPeriod>
<startDate>20230301</startDate>
<endDate>20230330</endDate>
</calculationPeriod>
</interestPaymentPeriod>
</interestFlows>
<capitalFlows>
<flow>
<flowSubType>AMO<flowSubType>
<date>20230116<date>
</flow>
<flow>
<flowSubType>AMO<flowSubType>
<date>20230220<date>
</flow>
<flow>
<flowSubType>INT<flowSubType>
<date>20230116<date>
</flow>
</capitalFlows>
</cashflows>
I have tried the followwing:
<xsl:template match="interestPaymentPeriod/calculationPeriod">
<xsl:variable name="found" select="'false'"></xsl:variable>
<xsl:variable name="pos" select="0"></xsl:variable>
<xsl:variable name="amortizingDate" select="''"></xsl:variable>
<xsl:variable name="position" select="0"></xsl:variable>
<xsl:variable name="path" select="../../../capitalFlows/flow" />
<xsl:for-each select="$path">
<xsl:variable name="pos" select="$pos + 1"></xsl:variable>
<xsl:if test="calculationStartDate < (../../../capitalFlows/flow[position()=$pos and flowSubType = 'AMO']/exdividendDate) and (../../../capitalFlows/flow[position()=$pos and flowSubType = 'AMO']/exdividendDate) < calculationEndDate ">
<xsl:variable name="found" select="'true'"></xsl:variable>
<xsl:variable name="position" select="$pos"></xsl:variable>
</xsl:if>
<xsl:if test="calculationStartDate < (../../../capitalFlows/flow[flowSubType = 'AMO']/exdividendDate) and (../../../capitalFlows/flow[flowSubType = 'AMO']/exdividendDate) < calculationEndDate ">
<xsl:variable name="found" select="'false'"></xsl:variable>
</xsl:if>
</xsl:for-each>
.....
But it seems that the section for-each select="../../../capitalflows/flow" does not seem to work as it did not even go inside the loop at all.
For info, the variable $path returns 3 (the total number of flow/ nodes) which is correct. So the path is correct. But somehow passing it through a for-each loop does not behave as expected as I am never getting inside the loop.
Any idea please ?
AFAICT you want to do:
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"/>
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="calculationPeriod">
<xsl:variable name="amo" select="/cashflows/capitalFlows/flow[flowSubType='AMO'][current()/startDate le date and date le current()/endDate]" />
<xsl:choose>
<xsl:when test="$amo">
<calculationPeriod>
<xsl:copy-of select="startDate"/>
<endDate>
<xsl:value-of select="$amo/date"/>
</endDate>
</calculationPeriod>
<calculationPeriod>
<startDate>
<xsl:value-of select="$amo/date"/>
</startDate>
<xsl:copy-of select="endDate"/>
</calculationPeriod>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
But the result in your example is different from what you show (even after fixing your input to be a well-formed XML).
Note that this assumes that for each calculationPeriod there can be at most one flow with intersecting date.
Related
I have an issue with the XSLT below. I need help to fix my transformation. I am using XSLT 1.0. Input can be 120KVA or 120MVA or 120.0KVA or 120.0KV. Output i want to parse into 3 parts. i.e.
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>K</tns:multiplier>
<tns:floatValue>120.0</tns:floatValue>
</tns:ratedApparentPower>
My current Transformation is:
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>
<xsl:value-of select="substring(translate(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,'1234567890', ''),1,1)" />
</tns:multiplier>
<tns:floatValue>
<xsl:value-of select="substring-before(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,substring(translate(//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE,'1234567890', ''),1,1))" />
</tns:floatValue>
</tns:ratedApparentPower>
</xsl:if>
Generated O/P:
<tns:ratedApparentPower>
<tns:unitSymbolUnit>VA</tns:unitSymbolUnit>
<tns:multiplier>.</tns:multiplier>
<tns:floatValue>120</tns:floatValue>
</tns:ratedApparentPower>
My doubts are:
How to get <tns:unitSymbolUnit>VA</tns:unitSymbolUnit>? Currently, I am hardcoding it. But it can be V or VA or any other value
How to get multiplier? With my current logic I get as <tns:multiplier>.</tns:multiplier> when I have 120.0 it works fine.
With my current logic I get <tns:floatValue>120</tns:floatValue> instead of 120.0.
Is there any way I can shorten the path (//ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE) by assigning it to some variable instead of using whole path every time?
Michael I edited your template belwo to match my requiremnts and usinga callTemplate but receive empty response
<xsl:template name="convertFloatValues">
<xsl:param name="floatValue1"/>
<xsl:variable name="unit" select="translate($floatValue1, '0123456789.', '')"/>
<unitSymbolUnit>
<xsl:value-of select="substring($unit, 2)"/>
</unitSymbolUnit>
<multiplier>
<xsl:value-of select="substring($unit, 1 , 1)"/>
</multiplier>
<floatValue>
<xsl:value-of select="substring-before(., $unit)"/>
</floatValue>
</xsl:template>
Call Template example . Not sure what I am missing. Can you please help me.
<tns:ratedApparentPower>
<xsl:call-template name="convertFloatValues">
<xsl:with-param name="floatValue1" select="/ns0:OutputParameters/ns0:XXJEAM_ASSET_SEARCH_PKG-24GETAS/ns0:ATTRIBUTE_GROUPS/ns0:ATTRIBUTE_GROUPS_ITEM[ns0:NAME='DISTXFR']/ns0:ATTRIBUTES/ns0:ATTRIBUTES_ITEM[ns0:NAME='KVA']/ns0:VALUE"/>
</xsl:call-template>
</tns:ratedApparentPower>
Input:
<OutputParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="*****">
<XXJEAM_ASSET_SEARCH_PKG-24GETAS>
<ATTRIBUTE_GROUPS_ITEM>
<NAME>DISTXFR</NAME>
<ATTRIBUTES>
<ATTRIBUTES_ITEM>
<COLUMN_NAME>C_ATTRIBUTE3</COLUMN_NAME>
<NAME>Volt</NAME>
<VALUE>500</VALUE>
</ATTRIBUTES_ITEM>
<ATTRIBUTES_ITEM>
<COLUMN_NAME>C_ATTRIBUTE4</COLUMN_NAME>
<NAME>KVA</NAME>
<VALUE>500.0KVA</VALUE>
</ATTRIBUTES_ITEM>
</ATTRIBUTES>
</ATTRIBUTE_GROUPS_ITEM>
</XXJEAM_ASSET_SEARCH_PKG-24GETAS>
</OutputParameters>
Consider the following example:
XML
<input>
<item>20KVA</item>
<item>120MVA</item>
<item>120.0KVA</item>
<item>120.0KV</item>
</input>
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:template match="/input">
<output>
<xsl:for-each select="item">
<xsl:variable name="unit" select="translate(., '0123456789.', '')" />
<ratedApparentPower>
<unitSymbolUnit>
<xsl:value-of select="substring($unit, 2)" />
</unitSymbolUnit>
<multiplier>
<xsl:value-of select="substring($unit, 1 , 1)" />
</multiplier>
<floatValue>
<xsl:value-of select="substring-before(., $unit)" />
</floatValue>
</ratedApparentPower>
</xsl:for-each>
</output>
</xsl:template>
</xsl:stylesheet>
Result
<?xml version="1.0" encoding="UTF-8"?>
<output>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>20</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>M</multiplier>
<floatValue>120</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>VA</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>120.0</floatValue>
</ratedApparentPower>
<ratedApparentPower>
<unitSymbolUnit>V</unitSymbolUnit>
<multiplier>K</multiplier>
<floatValue>120.0</floatValue>
</ratedApparentPower>
</output>
I Need to get the value in the XML file in between each key. For example, I have a list of keys to be used, and each key there's a corresponding output element. The keys can be placed anywhere, there is no proper order in where the key is needed to place. I need to do this in XSLT 2.0, and I don't have any idea on how will I do this.
Keys: Element:
/OPDH/ - ROOT/ELEMENT1/ABCD
/EKPH/ - ROOT/ELEMENT2/POIU
/SGDE/ - ROOT/ELEMENT3/WXYZ
...some other keys...
NOTE: Keys: is in BOLD, and Element is in ITALIC BOLD.
If I have a sample input like this:
1.)
<DATA>/OPDH/FLOWING SOLUTION/SGDE/Number0983713/EKPH/Sample test/some other keys/</DATA>
OR it can be:
2.)
<DATA>/some other keys/afdsf/SGDE/Number0983713/some other keys/PIHSAGA/OPDH/FLOWING SOLUTION/some other keys/No exception/EKPH/Sample test/some other keys/</DATA>
The expected output should look like this:
1.
<ROOT>
<ELEMENT1>
<ABCD>FLOWING SOLUTION</ABCD>
</ELEMENT1>
<ELEMENT2>
<POIU>Sample test</POIU>
</ELEMENT2>
<ELEMENT3>
<SGDE>Number0983713</SGDE>
</ELEMENT3>
...some other keys...
</ROOT>
2.
<ROOT>
...some other keys...
<ELEMENT3>
<SGDE>Number0983713</SGDE>
</ELEMENT3>
...some other keys...
<ELEMENT1>
<ABCD>FLOWING SOLUTION</ABCD>
</ELEMENT1>
...some other keys...
<ELEMENT2>
<POIU>Sample test</POIU>
</ELEMENT2>
...some other keys...
</ROOT>
Thank you.
Here is a partial suggestion that uses analyze-string:
<?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" xmlns:mf="http://example.com/mf"
exclude-result-prefixes="xs mf" version="2.0">
<xsl:param name="keys">
<element key="/OPDH/">ROOT/ELEMENT1/ABCD</element>
<element key="/EKPH/">ROOT/ELEMENT2/POIU</element>
<element key="/SGDE/">ROOT/ELEMENT3/WXYZ</element>
<element key="/some other keys/">ROOT/FOO/BAR</element>
</xsl:param>
<xsl:output indent="yes"/>
<xsl:variable name="pattern" as="xs:string"
select="concat('(', string-join($keys/element/#key, '|'), ')', '(.*?)', '(', string-join($keys/element/#key, '|'), ')')"/>
<xsl:key name="ref" match="element" use="#key"/>
<xsl:function name="mf:extract" as="element()*">
<xsl:param name="input" as="xs:string"/>
<xsl:analyze-string select="$input" regex="{$pattern}">
<xsl:matching-substring>
<xsl:if test="position() eq 1">
<element path="{key('ref', regex-group(1), $keys)}">
<xsl:value-of select="regex-group(2)"/>
</element>
<xsl:sequence
select="mf:extract(substring($input, string-length(concat(regex-group(1), regex-group(2))) + 1))"
/>
</xsl:if>
</xsl:matching-substring>
</xsl:analyze-string>
</xsl:function>
<xsl:template match="DATA">
<xsl:copy>
<xsl:sequence select="mf:extract(.)"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
This transforms the input
<?xml version="1.0" encoding="UTF-8"?>
<Root>
<DATA>/OPDH/FLOWING SOLUTION/SGDE/Number0983713/EKPH/Sample test/some other keys/</DATA>
<DATA>/some other keys/afdsf/SGDE/Number0983713/some other keys/PIHSAGA/OPDH/FLOWING SOLUTION/some other keys/No exception/EKPH/Sample test/some other keys/</DATA>
</Root>
into list of elements with the extracted data and the path to build:
<DATA>
<element path="ROOT/ELEMENT1/ABCD">FLOWING SOLUTION</element>
<element path="ROOT/ELEMENT3/WXYZ">Number0983713</element>
<element path="ROOT/ELEMENT2/POIU">Sample test</element>
</DATA>
<DATA>
<element path="ROOT/FOO/BAR">afdsf</element>
<element path="ROOT/ELEMENT3/WXYZ">Number0983713</element>
<element path="ROOT/FOO/BAR">PIHSAGA</element>
<element path="ROOT/ELEMENT1/ABCD">FLOWING SOLUTION</element>
<element path="ROOT/FOO/BAR">No exception</element>
<element path="ROOT/ELEMENT2/POIU">Sample test</element>
</DATA>
I am not quite sure whether that is doing the right job as I am not sure what determines the order and contents of the two samples you have provided and what e.g. /some other keys/ is meant to express. Tell us whether the result has the data you want or clarify your question and the samples you have shown. It should be easy to generate the XML from the above intermediary results once we have established that the right data is extracted.
You wrote very little about keys, so I assume that:
Your input file contains both:
key list (in KEYS tag),
actual source (in DATA tag).
Both these tags are children of the source ROOT tag.
KEYS tag contains in each row a pair of key value and output path, where
respective content for this key shoud be placed.
Assume that your full input is:
<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
<KEYS>
/OPDH/ - ROOT/ELEMENT1/ABCD
/EKPH/ - ROOT/ELEMENT2/POIU
/SGDE/ - ROOT/ELEMENT3/SGDE
</KEYS>
<DATA>/OPDH/FLOWING SOLUTION/SGDE/Number0983713/EKPH/Sample test/</DATA>
</ROOT>
Then you can write the XSLT as follows:
<?xml version="1.0" encoding="UTF-8" ?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="ROOT">
<xsl:copy>
<!-- Divide KEYS into rows -->
<xsl:variable name="keys_1" select="tokenize(KEYS, '
')"/>
<!-- # of rows -->
<xsl:variable name="nn" select="count($keys_1)"/>
<!-- Drop 1st and last (empty) row -->
<xsl:variable name="keys" select="subsequence($keys_1, 2, $nn - 2)"/>
<!-- Divide DATA into tokens -->
<xsl:variable name="data_1" select="tokenize(DATA, '/')"/>
<!-- # of tokens -->
<xsl:variable name="nn" select="count($data_1)"/>
<!-- Drop 1st and last (empty) token -->
<xsl:variable name="data" select="subsequence($data_1, 2, $nn - 2)"/>
<!-- Generate output data for each row from keys -->
<xsl:for-each select="$keys">
<!-- Divide the keys row into tokens -->
<xsl:variable name="parts" select="tokenize(., '/')"/>
<!-- # of tokens -->
<xsl:variable name="nn" select="count($parts)"/>
<!-- Source key - token No 2 (after the 1st '/') -->
<xsl:variable name="srcKey" select="$parts[2]"/>
<!-- path - tokens after 'ROOT' -->
<xsl:variable name="path" select="subsequence($parts, 4)"/>
<!-- Open tags given in path -->
<xsl:for-each select="$path">
<xsl:text>
</xsl:text>
<!-- Spacing -->
<xsl:variable name="nn" select="position()"/>
<xsl:value-of select=
"string-join((for $i in 1 to $nn return ' '), '')"/>
<!-- Print opening tag -->
<xsl:value-of select="concat('<', ., '>')"
disable-output-escaping="yes"/>
</xsl:for-each>
<!-- Find position of the source key in data -->
<xsl:variable name="ind" select="index-of($data, $srcKey)[1]"/>
<!-- Get data from the next token -->
<xsl:value-of select="$data[$ind + 1]"/>
<!-- Close tags given in path -->
<xsl:for-each select="reverse($path)">
<xsl:variable name="nn" select="position()"/>
<!-- Spacing and NewLine - but not for the most inner tag -->
<xsl:if test="$nn > 1">
<xsl:text>
</xsl:text>
<xsl:value-of select=
"string-join((for $i in 1 to last() - $nn + 1 return ' '), '')"/>
</xsl:if>
<!-- Print closing tag -->
<xsl:value-of select="concat('</', ., '>')"
disable-output-escaping="yes"/>
</xsl:for-each>
</xsl:for-each>
<xsl:text>
</xsl:text>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy><xsl:apply-templates select="#*|node()"/></xsl:copy>
</xsl:template>
</xsl:transform>
We have a requirement where we are getting an XML payload in form of String for one node and we need to expand this XML and pass the XML node values to destination XSD columns.
For example Input XSD :
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Transaction xmlns="http://xmlns.oracle.com/2012/SCEM">
<TRXKEY>0x0000013</TRXKEY>
<EVENTNAME>SalesOrder</EVENTNAME>
<ACCEPTEDQUANTITY>0</ACCEPTEDQUANTITY>
<ORDEREDQUANTITY>0</ORDEREDQUANTITY>
<SOLORGID>0</SOLORGID>
<LHORDERHOLDIDRELEASED>0</LHORDERHOLDIDRELEASED>
<ORDEREDQUANTITYUOM>0</ORDEREDQUANTITYUOM>
<UNITSELLINGPRICE>0</UNITSELLINGPRICE>
<ORDERTYPE><Val lang='AR'>OrderTypeARARARAR</Val>
<Val lang='KO'>OrderTypeKOKOKOKO</Val>
<Val lang='US'>OrderTypeUSUSUUS</Val>
</ORDERTYPE>
</Transaction>
Output XML
<Transaction1 xmlns="http://xmlns.oracle.com/2012/SCEM">
<TRXKEY>0x0000013</TRXKEY>
<EVENTNAME>SalesOrder</EVENTNAME>
<ACCEPTEDQUANTITY>0</ACCEPTEDQUANTITY>
<ORDEREDQUANTITY>0</ORDEREDQUANTITY>
<SOLORGID>0</SOLORGID>
<LHORDERHOLDIDRELEASED>0</LHORDERHOLDIDRELEASED>
<ORDEREDQUANTITYUOM>0</ORDEREDQUANTITYUOM>
<UNITSELLINGPRICE>0</UNITSELLINGPRICE>
<ORDERTYPE_AR>OrderTypeARARARAR</ORDERTYPE_AR>
<ORDERTYPE_US>OrderTypeUSUSUUS</ORDERTYPE_US>
<ORDERTYPE_KO>OrderTypeKOKOKOKO</ORDERTYPE_KO>
</Transaction>
We need to loop through the ORDERTYPE node XML string. Depending on the 'Val lang=' value we need to pass this node value to output XSD ORDERTYPE_$lang value column.
How can I loop through this XML string for these 2 different values and pass it to the corresponding output XML column?
I was thinking of using split but it is not helping much.
Thanks Martin,
I have come up with below code which resolved my issue.
<xsl:stylesheet version="1.0"
xmlns:bpws="http://schemas.xmlsoap.org/ws/2003/03/business-process/"
xmlns:xp20="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.Xpath20"
xmlns:mhdr="http://www.oracle.com/XSL/Transform/java/oracle.tip.mediator.service.common.functions.MediatorExtnFunction"
xmlns:bpel="http://docs.oasis-open.org/wsbpel/2.0/process/executable"
xmlns:oraext="http://www.oracle.com/XSL/Transform/java/oracle.tip.pc.services.functions.ExtFunc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:dvm="http://www.oracle.com/XSL/Transform/java/oracle.tip.dvm.LookupValue"
xmlns:hwf="http://xmlns.oracle.com/bpel/workflow/xpath"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:med="http://schemas.oracle.com/mediator/xpath"
xmlns:ids="http://xmlns.oracle.com/bpel/services/IdentityService/xpath"
xmlns:bpm="http://xmlns.oracle.com/bpmn20/extensions"
xmlns:client="http://xmlns.oracle.com/KeyStore/emsProjXslt/BPELProcess1"
xmlns:xdk="http://schemas.oracle.com/bpel/extension/xpath/function/xdk"
xmlns:xref="http://www.oracle.com/XSL/Transform/java/oracle.tip.xref.xpath.XRefXPathFunctions"
xmlns:ns2="http://xmlns.oracle.com/2012/SCEM"
xmlns:plnk="http://schemas.xmlsoap.org/ws/2003/05/partner-link/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:ora="http://schemas.oracle.com/xpath/extension"
xmlns:socket="http://www.oracle.com/XSL/Transform/java/oracle.tip.adapter.socket.ProtocolTranslator"
xmlns:ldap="http://schemas.oracle.com/xpath/extension/ldap"
exclude-result-prefixes="xsi xsl client ns2 plnk xsd wsdl bpws xp20 mhdr bpel oraext dvm hwf med ids bpm xdk xref ora socket ldap">
<xsl:template match="/">
<ns2:Transaction>
<ns2:TRXKEY>
<xsl:value-of select="/ns2:Transaction/ns2:TRXKEY"/>
</ns2:TRXKEY>
<xsl:call-template name="for.loop.Parameters">
<xsl:with-param name="sourceNodes"
select='substring-after(/ns2:Transaction/ns2:ORDERTYPE,"Val lang=")'/>
</xsl:call-template>
</ns2:Transaction>
</xsl:template>
<xsl:template name="for.loop.Parameters">
<xsl:param name="sourceNodes"/>
<xsl:variable name="temp">
<xsl:choose>
<xsl:when test="string-length($sourceNodes) > '0'">
<xsl:value-of select="substring-before($sourceNodes,'</Val>')"/>
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="Expression" select="substring-after($temp, '>')"/>
<xsl:variable name="Expression1" select="substring-before($temp, '>')"/>
<xsl:if test="contains($Expression1,'AR')">
<ns2:ORDERTYPE_AR>
<xsl:value-of select="$Expression"/>
</ns2:ORDERTYPE_AR>
</xsl:if>
<xsl:if test="contains($Expression,'US')">
<ns2:ORDERTYPE_US>
<xsl:value-of select="$Expression"/>
</ns2:ORDERTYPE_US>
</xsl:if>
<xsl:if test="contains($Expression,'KO')">
<ns2:ORDERTYPE_KO>
<xsl:value-of select="$Expression"/>
</ns2:ORDERTYPE_KO>
</xsl:if>
<xsl:variable name="test">
<xsl:value-of select="substring-after($sourceNodes,'/Val>')"/>
</xsl:variable>
<xsl:if test="string-length($test) > 1 ">
<xsl:call-template name="for.loop.Parameters">
<xsl:with-param name="sourceNodes">
<xsl:value-of select='substring-after($test,"Val lang=")'/>
</xsl:with-param>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
I need the maximum of three three kinds of values.
I've got a structure similar to this.
note:the first two answers are based on a previous example xml (in set 2 of night the max-above-average was 8). This was confusing, so I changed it to 7.
<data>
<record>
<max>60</max>
</record>
<day>
<set>
<average>49</average>
<max-above-average>3</max-above-average>
</set>
<set>
<average>45</average>
<max-above-average>9</max-above-average>
</set>
</day>
<night>
<set>
<average>50</average>
<max-above-average>5</max-above-average>
</set>
<set>
<average>52</average>
<max-above-average>7</max-above-average>
</set>
</night>
</data>
Now I need the maximum of the record, day and night. This would be maximum: 60, the value of the record in this example: 60 = 60, > 49 + 3, 45 +9, 50 + 5, 52+7. Day and night maximums need to be calculated. Because of this
max(//record/max | //day/set/(average + max-above-average)) | //night/set/(average +max-above-average))
does not work. The |-sign only works for nodes.
It gives following error:
Required item type of second operand of '|' is node(); supplied value has item type xs:double
I'm using xpath 2.0 and xslt 2.0.
Here are the wanted two XPath 2.0 expressions (for producing the "max" and the "min" value, respectively):
max(/*/(day|night)/*/(average+max-above-average))
and
min(/*/(day|night)/*/(average -min-above-average))
XSLT 2.0 - based verification:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
max: <xsl:text/>
<xsl:sequence select=
"max(/*/(day|night)/*/(average+max-above-average))"/>
min: <xsl:text/>
<xsl:sequence select=
"min(/*/(day|night)/*/(average -min-above-average))"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<data>
<record>
<min>40</min>
<max>60</max>
</record>
<day>
<set>
<average>49</average>
<max-above-average>3</max-above-average>
<min-above-average>15</min-above-average>
</set>
<set>
<average>45</average>
<max-above-average>9</max-above-average>
<min-above-average>2</min-above-average>
</set>
</day>
<night>
<set>
<average>50</average>
<max-above-average>5</max-above-average>
<min-above-average>6</min-above-average>
</set>
<set>
<average>52</average>
<max-above-average>8</max-above-average>
<min-above-average>11</min-above-average>
</set>
</night>
</data>
the two XPath expressions are evaluated and the results of these evaluations are copied to the output:
max: 60
min: 34
Update:
The OP says in a comment that he wants "maximum of day and night ànd record" -- I really don't understand what he means by that.
Here is my attempt at guessing:
max(
(/*/record/max,
/*/(day|night)/*/(average+max-above-average, average+min-above-average)
)
)
When implanted in the XSLT transformation (above), this produces:
max: 64
An XSLT 1.0 approach:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="yes" omit-xml-declaration="yes"/>
<xsl:template match="/*">
<xsl:variable name="recordMax" select="record/max" />
<xsl:variable name="dayMax">
<xsl:apply-templates select="day" mode="max" />
</xsl:variable>
<xsl:variable name="nightMax">
<xsl:apply-templates select="night" mode="max" />
</xsl:variable>
<xsl:call-template name="Max">
<xsl:with-param name="v1" select="$recordMax" />
<xsl:with-param name="v2">
<xsl:call-template name="Max">
<xsl:with-param name="v1" select="$dayMax" />
<xsl:with-param name="v2" select="$nightMax" />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template match="*[set]" mode="max">
<xsl:apply-templates select="set" mode="max">
<xsl:sort select="average + max-above-average"
data-type="number"
order="descending" />
</xsl:apply-templates>
</xsl:template>
<xsl:template match="set" mode="max">
<xsl:if test="position() = 1">
<xsl:value-of select="average + max-above-average" />
</xsl:if>
</xsl:template>
<xsl:template name="Max">
<xsl:param name="v1" />
<xsl:param name="v2" />
<xsl:choose>
<xsl:when test="not($v2 > $v1)">
<xsl:value-of select="$v1" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$v2" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
When run on your sample input, the result is:
60
When I try to recursive sum an attributes from multiple nodes, it's gluing like string :(
XML-file (second mileage-node include first mileage-node)
<mileage value="15000">
<operation title="Replacing the engine oil" cost="500" />
<sparepart title="Oil filter" cost="250" />
<sparepart title="Motor oil" cost="1050" />
</mileage>
<mileage value="30000">
<repeating mileage="15000" />
<operation title="Replacement of spark" cost="1200" />
</mileage>
XSL-template
<xsl:template match="mileage[#value]">
<xsl:param name="sum" select="number(0)" />
<xsl:variable name="milinkage"><xsl:value-of select="number(repeating/#mileage)" /></xsl:variable>
<xsl:apply-templates select="parent::*/mileage[#value=$milinkage]"><xsl:with-param name="sum" select="number($sum)" /></xsl:apply-templates>
<xsl:value-of select="number(sum(.//#cost))"/> <!-- + number($sum) -->
</xsl:template>
Glued result is 18001200, but I want see 3000 (1800 + 1200)
Please tell me what is wrong here?
Thanx!
Remove the dot and you will always see 3000 because all #costs (independent from starting point) will be summed.
<xsl:value-of select="number(sum(//#cost))"/> <!-- + number($sum) -->
Output will look like this: 30003000
But I assume that something is wrong with your approach. When you call a template recursive then the output will also will be printed as much as the template calls itself in your case. You need to print out the result at the end of your recursion
Given this input:
<root>
<mileage value="15000">
<operation title="Replacing the engine oil" cost="500" />
<sparepart title="Oil filter" cost="250" />
<sparepart title="Motor oil" cost="1050" />
</mileage>
<mileage value="30000">
<repeating mileage="15000" />
<operation title="Replacement of spark" cost="1200" />
</mileage>
</root>
and using this xslt:
<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:template match="/">
<xsl:apply-templates select="root"/>
</xsl:template>
<xsl:template match="root">
<xsl:apply-templates select="mileage[#value=30000]"/>
</xsl:template>
<xsl:template match="mileage[#value]">
<xsl:param name="sum" select="number(0)" />
<xsl:variable name="milinkage"><xsl:value-of select="number(repeating/#mileage)" /></xsl:variable>
<xsl:variable name="newsum">
<xsl:value-of select="number(sum(.//#cost)) + $sum"/>
</xsl:variable>
<xsl:apply-templates select="parent::*/mileage[#value=$milinkage]"><xsl:with-param name="sum" select="number($newsum)" /></xsl:apply-templates>
<xsl:if test="not(parent::*/mileage[#value=$milinkage])">
<xsl:value-of select="$newsum"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
gives the correct result: 3000
You need xmlns:exsl="http://exslt.org/common"
<xsl:template match="/">
<xsl:variable name="nodes">
<xsl:apply-templates select="root/mileage[position()=last()]"/>
</xsl:variable>
<xsl:copy-of select="sum(exsl:node-set($nodes)/*[#cost]/#cost)"/>
</xsl:template>
<xsl:template match="mileage">
<xsl:copy-of select="*[#cost]"/>
<xsl:apply-templates select="../mileage[#value=current()/repeating/#mileage]"/>
</xsl:template>`