Following is the XML piece of code -
<groups>
<group i=1>
<member t="P.M" c="Y">ABC</member>
<member t="P.L">PQR</member>
<member t="M">XYZ</member>
</group>
<group i=2>
<member t="M" c="Y">ABC</member>
<member t="M">PQR</member>
</group>
<group i=3>
<member t="P.L" c="Y">ABC</member>
<member t="M">PQR</member>
<member t="M">XYZ</member>
</group>
<group i=4>
<member t="M">ABC</member>
<member t="M" c="Y">PQR</member>
</group>
<group i=5>
<member t="M">ABC</member>
<member t="M" c="Y">PQR</member>
<member t="M" c="Y">XYZ</member>
</group>
<group i=6>
<member t="M" dec="Y">ABC</member>
</group>
</groups>
Desired HTML output using XSLT 1.0 -
<U>ABC</U>, P.M, PQR, P.L and XYZ, M
<U>ABC</U> and PQR, MM
<U>ABC</U>, P.L, PQR and XYZ, MM
ABC and <U>PQR</U>, MM
ABC, <U>PQR</U> and <U>XYZ</U>, MM
<U>ABC</U>, M
The partial XSLT solution for the above output is -
<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='html' media-type='text/html'/>
<xsl:template match="/">
<html>
<body>
<xsl:apply-templates/>
</body>
</html>
</xsl:template>
<xsl:template match="group">
<p>
<xsl:apply-templates select="#*|node()"/>
</p>
</xsl:template>
<xsl:template match="member">
<xsl:choose>
<xsl:when test='#c = "Y"'>
<u><xsl:value-of select="."/></u>, <xsl:value-of select='#t'/>
</xsl:when>
<xsl:otherwise>
<b><xsl:value-of select="."/></b>, <xsl:value-of select='#t'/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="member[following-sibling::member]">
<xsl:choose>
<xsl:when test='#c = "Y"'>
<u><xsl:value-of select="."/></u>, <xsl:value-of select='#t'/>
<xsl:text> and </xsl:text>
</xsl:when>
<xsl:otherwise>
<b><xsl:value-of select="."/></b>, <xsl:value-of select='#t'/>
<xsl:text> and </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="member[following-sibling::member[following-sibling::member]]">
<xsl:choose>
<xsl:when test='#c = "Y"'>
<u><xsl:value-of select="."/></u>, <xsl:value-of select='#t'/>
<xsl:text>, </xsl:text>
</xsl:when>
<xsl:otherwise>
<b><xsl:value-of select="."/></b>, <xsl:value-of select='#t'/>
<xsl:text>, </xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The above XSLT is giving the output in correct format as required, but if the <member>
are of same type then how to add this type at the end..?
In answer to your immediate question though, to output the correct number of t elements for a member, you could do something like this:
<xsl:apply-templates select="../member/#t[. = current()/#t]"/>
So, it will output a value for each matching #t attribute.
However, I think you need to read up more on grouping, as Mr. Michael Kay suggested in your last question.
In this case, you are grouping by a group attributeand a member attribute. So, you would define a key, like so
<xsl:key name="members" match="member" use="concat(../#i, '|', #t)"/>
Do note the use of the pipe | to concatenate the two parts of the key. You would need to pick a character that could not occur in either part of the key.
Then, you can get the first element of each group like so
<xsl:apply-templates
select="group/member[generate-id() = generate-id(key('members', concat(../#i, '|', #t))[1])]"
mode="group"/>
And to iterate over all the elements in the group, you can then do this...
<xsl:apply-templates select="key('members', concat(../#i, '|', #t))"/>
So, given the following XML
<groups>
<group i="1">
<member t="P.M" c="Y">ABC</member>
<member t="P.L">PQR</member>
<member t="M">XYZ</member>
</group>
<group i="2">
<member t="M" c="Y">ABC</member>
<member t="M">PQR</member>
</group>
<group i="3">
<member t="P.L" c="Y">ABC</member>
<member t="M">PQR</member>
<member t="M">XYZ</member>
</group>
<group i="4">
<member t="M">ABC</member>
<member t="M" c="Y">PQR</member>
</group>
<group i="5">
<member t="M">ABC</member>
<member t="M" c="Y">PQR</member>
<member t="M" c="Y">XYZ</member>
</group>
<group i="6">
<member t="M" c="Y">ABC</member>
</group>
</groups>
Using the following XSLT....
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:key name="members" match="member" use="concat(../#i, '|', #t)"/>
<xsl:template match="/groups">
<xsl:apply-templates select="group/member[generate-id() = generate-id(key('members', concat(../#i, '|', #t))[1])]" mode="group"/>
</xsl:template>
<xsl:template match="member" mode="group">
<xsl:apply-templates select="key('members', concat(../#i, '|', #t))"/>
<xsl:text>, </xsl:text>
<xsl:apply-templates select="../member/#t[. = current()/#t]"/>
<xsl:variable name="others" select="count(following-sibling::member[#t != current()/#t])" />
<xsl:choose>
<xsl:when test="$others > 1">, </xsl:when>
<xsl:when test="$others = 1"> and </xsl:when>
<xsl:otherwise>
<xsl:text>
</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="member">
<xsl:choose>
<xsl:when test="#c='Y'">
<u>
<xsl:value-of select="."/>
</u>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
<xsl:variable name="others" select="count(following-sibling::member[#t = current()/#t])" />
<xsl:choose>
<xsl:when test="$others > 1">, </xsl:when>
<xsl:when test="$others = 1"> and </xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The following is output....
<u>ABC</u>, P.M, PQR, P.L and XYZ, M
<u>ABC</u> and PQR, MM
<u>ABC</u>, P.L, PQR and XYZ, MM
ABC and <u>PQR</u>, MM
ABC, <u>PQR</u> and <u>XYZ</u>, MMM
<u>ABC</u>, M
Related
I want to convert a XML to JSON using XSLT. But I am facing few issues.
Input XML
<notifications xmlns="http://soap.sforce.com/2005/09/outbound">
<OrganizationId>123</OrganizationId>
<ActionId>123</ActionId>
<SessionId xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
<EnterpriseUrl>qwe</EnterpriseUrl>
<PartnerUrl>qwe</PartnerUrl>
<Notification>
<Id>123</Id>
<sObject xsi:type="sf:Opportunity" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:sf="urn:sobject.enterprise.soap.sforce.com">
<sf:Id>ao123</sf:Id>
<sf:Amount>60000.0</sf:Amount>
<sf:CreatedDate>2014-11-26T14:45:52.000Z</sf:CreatedDate>
<sf:IsClosed>false</sf:IsClosed>
</sObject>
</Notification>
</notifications>
Expected Output JSON
{
"notifications": {
"OrganizationId": "123",
"ActionId": "123",
"SessionId": {
"#nil": "true"
},
"EnterpriseUrl": "qwe",
"PartnerUrl": "qwe",
"Notification": [
{
"Id": "ao123",
"sObject": {
"#type": "sf:Opportunity",
"Id": "ao123",
"Amount": "60000.0",
"CreatedDate": "2014-11-26T14:45:52.000Z",
"IsClosed": "false"
}
}
]
}
}
From this answer I got XSLT and I have tried it. This is the XSLT code I have tried. Fiddle link
<?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"
xmlns="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="json-xml">
<xsl:apply-templates/>
</xsl:variable>
<xsl:value-of select="xml-to-json($json-xml, map { 'indent' : true() })"/>
</xsl:template>
<xsl:template match="*[not(*)]">
<string key="{local-name()}">{.}</string>
</xsl:template>
<xsl:template match="*[*]">
<xsl:param name="key" as="xs:boolean" select="false()"/>
<map>
<xsl:if test="$key">
<xsl:attribute name="key" select="local-name()"/>
</xsl:if>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:choose>
<xsl:when test="current-group()[2]">
<array key="{local-name()}">
<xsl:apply-templates select="current-group()">
<xsl:with-param name="key" select="false()"/>
</xsl:apply-templates>
</array>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()">
<xsl:with-param name="key" select="true()"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</map>
</xsl:template>
</xsl:stylesheet>
So below are few issues which I am facing
notifications node is missing in json output, it is the root node in the xml.
Notification should be a json array even if I receive one
item in XML
Please note that I don't want to hard code node names other than notifications and Notification in XSLT code as I may receive different nodes under node Notification.
I am looking for XSLT which can handle my requirements
Some of the requirements (outer map/JSON object, always making Notification an array) can of course be inserted into the XSLT code by modifying what you linked to (please make sure next time you show the XSLT in the question as well):
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="json-xml">
<map>
<xsl:apply-templates/>
</map>
</xsl:variable>
<xsl:value-of select="xml-to-json($json-xml, map { 'indent' : true() })"/>
</xsl:template>
<xsl:template match="*[not(*)]">
<string key="{local-name()}">{.}</string>
</xsl:template>
<xsl:template match="*[*]">
<xsl:param name="key" as="xs:boolean" select="true()"/>
<map>
<xsl:if test="$key">
<xsl:attribute name="key" select="local-name()"/>
</xsl:if>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:choose>
<xsl:when test="current-group()[2] or current-grouping-key() = QName('http://soap.sforce.com/2005/09/outbound', 'Notification')">
<array key="{local-name()}">
<xsl:apply-templates select="current-group()">
<xsl:with-param name="key" select="false()"/>
</xsl:apply-templates>
</array>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()">
<xsl:with-param name="key" select="true()"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</map>
</xsl:template>
</xsl:stylesheet>
The code you grabbed from another question/answer I think was not written with XML having elements with attributes in mind; I have tried to adapt it below with
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
exclude-result-prefixes="#all"
xmlns="http://www.w3.org/2005/xpath-functions"
expand-text="yes"
version="3.0">
<xsl:output method="text"/>
<xsl:template match="/">
<xsl:variable name="json-xml">
<map>
<xsl:apply-templates/>
</map>
</xsl:variable>
<xsl:value-of select="xml-to-json($json-xml, map { 'indent' : true() })"/>
</xsl:template>
<xsl:template match="*[not(*)]">
<string key="{local-name()}">{.}</string>
</xsl:template>
<xsl:template match="#*">
<string key="#{local-name()}">{.}</string>
</xsl:template>
<xsl:template match="*[* or #*]">
<xsl:param name="key" as="xs:boolean" select="true()"/>
<map>
<xsl:if test="$key">
<xsl:attribute name="key" select="local-name()"/>
</xsl:if>
<xsl:apply-templates select="#*"/>
<xsl:for-each-group select="*" group-by="node-name()">
<xsl:choose>
<xsl:when test="current-group()[2] or current-grouping-key() = QName('http://soap.sforce.com/2005/09/outbound', 'Notification')">
<array key="{local-name()}">
<xsl:apply-templates select="current-group()">
<xsl:with-param name="key" select="false()"/>
</xsl:apply-templates>
</array>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="current-group()">
<xsl:with-param name="key" select="true()"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each-group>
</map>
</xsl:template>
</xsl:stylesheet>
That seems to give the wanted attributes as #name properties of a JSON object/map for your sample; I can't guarantee it will work in general.
I add all the nodes' attributes which have the same index atrribute. I multiply this sum by a particular number (extracted from a secontary xml) and I have the step_1 product. I repeat the proceedure multiplying each time by a different particular number, and I add the new product to the product of the previous step and so on up to the last node. But in each step of the loop the sum functions looses the runningsum keeping only the current product. I would appreciate any help.
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="koinohrista.xsl"?>
<Features>
<Feature code="a" price="1"></Feature>
<Feature code="a" price="2"></Feature>
<Feature code="b" price="3"></Feature>
<Feature code="b" price="4"></Feature>
<Feature code="c" price="5"></Feature>
</Features>
The secondary xml
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="hiliosta.xsl"?>
<Features>
<Feature name="a1" koinoh="10" elev="20" heat="30"></Feature>
</Features>
The xsl
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:variable name="Inventory" select="document('C:\Kapopoulos\Inventory.xml')"/>
<xsl:key name="KeyCode" match="Feature" use="#code"/>
<xsl:template match="Features">
<xsl:call-template name="sum">
<xsl:with-param name="nodes" select="Feature"/>
<xsl:with-param name="sum" select="0"/>
</xsl:call-template>
</xsl:template>
<!-- Sum Function -->
<xsl:template name="sum">
<xsl:param name="nodes"/>
<xsl:param name="sum"/>
<xsl:variable name="curr" select="$nodes[1]"/>
$sum1 = <xsl:value-of select="$sum"/><br/> <!-- for view reasons -->
<table>
<xsl:for-each select="Feature[generate-id() = generate-id(key('KeyCode',#code)[1])]">
<xsl:sort select="#code" order="ascending"/>
<xsl:if test="$curr">
Step1 = <xsl:value-of select="$sum"/> ||| <!-- for view reasons -->
<xsl:variable name="attrName">
<xsl:choose>
<xsl:when test="#code='a' or #code='c' or #code='d'">koinoh</xsl:when>
<xsl:when test="#code='b'">elev</xsl:when>
<xsl:when test="#code='e'">heat</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="runningsum" select="$sum + sum(key('KeyCode',#code)/#price)
* $Inventory/*/Feature[#name='a1']/#*[name()=$attrName] div sum($Inventory//Feature/#*[name()=$attrName])"/>
RunSum2 = <xsl:value-of select="$runningsum"/><br/> <!-- for view reasons -->
<xsl:call-template name="sum">
<xsl:with-param name="nodes" select="$nodes[position() > 1]"/>
<xsl:with-param name="sum" select="$runningsum"/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
<xsl:if test="not($curr)"> Final=
<xsl:value-of select="$sum"/>
</xsl:if>
</table>
</xsl:template>
</xsl:stylesheet>
Desired calculation steps and output
1) SumOfPrice for #code="a" : 1+2=3
2) ProductOfSum : 3 * "koinoh"=3*10=30
3) RunningSum = 30
4) SumOfPrice for #code="b" : 3+4=7
5) ProductOfSum : 7 * "#elev"=7*20=140
6) RunningSum = 30+140=170
7) SumOfPrice for #code="c" : 5
8) ProductOfSum : 5 * "#heat" = 5*30=150
6) RunningSum = 30+140+150=320
DesiredOutput = 320
To get only the final result of 320, you could skip the grouping part and just process each individual Feature on its own. However, it might be useful to minimize the lookups to the "Inventory" document, so let's keep the grouping:
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:param name="path-to-inventory">C:\Kapopoulos\Inventory.xml</xsl:param>
<xsl:variable name="inventory" select="document($path-to-inventory)/Features/Feature[#name='a1']"/>
<xsl:key name="feature-by-code" match="Feature" use="#code" />
<xsl:template match="/Features">
<result>
<xsl:call-template name="summarize">
<!-- select distinct codes only -->
<xsl:with-param name="items" select="Feature[count(. | key('feature-by-code', #code)[1]) = 1]"/>
</xsl:call-template>
</result>
</xsl:template>
<xsl:template name="summarize">
<xsl:param name="items" select="/.."/>
<xsl:param name="balance" select="0"/>
<xsl:choose>
<xsl:when test="$items">
<xsl:variable name="code" select="$items[1]/#code" />
<xsl:variable name="sum-price" select="sum(key('feature-by-code', $code)/#price)" />
<xsl:variable name="factor">
<xsl:choose>
<xsl:when test="$code='a'">
<xsl:value-of select="$inventory/#koinoh" />
</xsl:when>
<xsl:when test="$code='b'">
<xsl:value-of select="$inventory/#elev" />
</xsl:when>
<xsl:when test="$code='c'">
<xsl:value-of select="$inventory/#heat" />
</xsl:when>
</xsl:choose>
</xsl:variable>
<xsl:variable name="product" select="$sum-price * $factor" />
<!-- recursive call -->
<xsl:call-template name="summarize">
<xsl:with-param name="items" select="$items[position() > 1]"/>
<xsl:with-param name="balance" select="$balance + $product"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$balance" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I have a partially working XSLT that groups my elements by columns according to column break. My elements are displayed in one column until a <ColumnBreak> is encountered, which then makes the succeeding items rendered in a new column to the right.
Now I want to extend this XSLT to achieve the concept of column span based on the width of my items, so I followed the sample implementation from this topic. Furthermore, if a region is already occupied in the next column due to the width span of the previous element, the succeeding elements following the ColumnBreak shall be displayed below the already occupied region.
XML:
<?xml version="1.0" encoding="utf-8" ?>
<Group>
<Items>
<Item>
<Display>Item 1</Display>
</Item>
<Item>
<Display>Item 2</Display>
<Width>2</Width>
</Item>
<Item>
<Display>Item 3</Display>
</Item>
<ColumnBreak />
<Item>
<Display>Item 4</Display>
</Item>
<Item>
<Display>Item 5</Display>
</Item>
<ColumnBreak />
<Item>
<Display>Item 6</Display>
</Item>
<Item>
<Display>Item 7</Display>
</Item>
</Items>
</Group>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:msxsl="urn:schemas-microsoft-com:xslt"
xmlns:exsl="http://exslt.org/common" extension-element-prefixes="exsl"
exclude-result-prefixes="msxsl xsi">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="Group">
<xsl:apply-templates select="Items"/>
</xsl:template>
<xsl:key name="cell-by-row" match="cell" use="#row" />
<xsl:key name="cell-by-col" match="cell" use="concat(#row, '|', #col)" />
<xsl:template match="Items">
<div>
<xsl:variable name="cells">
<xsl:apply-templates select="*[1]" mode="item-sibling">
<xsl:with-param name="row" select="1"/>
<xsl:with-param name="col" select="1"/>
<xsl:with-param name="index" select="1"/>
</xsl:apply-templates>
</xsl:variable>
<xsl:call-template name="generate-rows">
<xsl:with-param name="cells" select="$cells" />
<xsl:with-param name="current-row" select="1"/>
</xsl:call-template>
</div>
</xsl:template>
<xsl:template name="generate-rows">
<xsl:param name="cells" />
<xsl:param name="current-row" />
<xsl:param name="last-row" />
<xsl:for-each select="exsl:node-set($cells)/cell[count(. | key('cell-by-row', #row)[1]) = 1]">
<xsl:for-each select="key('cell-by-row', #row)[count(. | key('cell-by-col', concat(#row, '|', #col))[1]) = 1]">
<xsl:for-each select="key('cell-by-col', concat(#row, '|', #col))">
<xsl:if test="not(#empty)">
<div row="{#row}" col="{#col}" index="{#index}" width="{#width}">
<xsl:apply-templates select="." mode="item">
</xsl:apply-templates>
</div>
</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
<xsl:template match="*" mode="item-sibling">
<xsl:param name="row"/>
<xsl:param name="col"/>
<xsl:param name="index"/>
<xsl:param name="previous-cells" select="some-dummy-node-to-make-this-a-node"/>
<xsl:variable name="width">
<xsl:call-template name="width-template" />
</xsl:variable>
<xsl:variable name="colliding-cell" select="exsl:node-set($previous-cells)/cell[#row = $row and #col < $col and #index > $index and #width + #col > $col]" />
<xsl:choose>
<xsl:when test="$colliding-cell">
<xsl:apply-templates select="self::node()" mode="item-sibling">
<xsl:with-param name="row" select="$row"/>
<xsl:with-param name="col" select="$col"/>
<xsl:with-param name="index" select="$colliding-cell/#index + 1"/>
<xsl:with-param name="previous-cells" select="$previous-cells"/>
</xsl:apply-templates>
</xsl:when>
<xsl:otherwise>
<xsl:variable name="current-cell">
<cell row="{$row}" col="{$col}" index="{$index}" width="{$width}">
<xsl:copy-of select="exsl:node-set(.)"/>
</cell>
</xsl:variable>
<xsl:variable name="new-cells">
<xsl:copy-of select="$previous-cells"/>
<xsl:copy-of select="$current-cell"/>
</xsl:variable>
<xsl:copy-of select="$current-cell"/>
<xsl:apply-templates select="following-sibling::*[1]" mode="item-sibling">
<xsl:with-param name="row" select="$row"/>
<xsl:with-param name="col" select="$col"/>
<xsl:with-param name="index" select="$index + 1"/>
<xsl:with-param name="previous-cells" select="$new-cells"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="ColumnBreak" mode="item-sibling">
<xsl:param name="row"/>
<xsl:param name="col"/>
<xsl:param name="index"/>
<xsl:param name="previous-cells"/>
<xsl:apply-templates select="following-sibling::*[1]" mode="item-sibling">
<xsl:with-param name="row" select="$row"/>
<xsl:with-param name="col" select="$col + 1"/>
<xsl:with-param name="index" select="1" />
<xsl:with-param name="previous-cells" select="$previous-cells"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template name="width-template">
<xsl:choose>
<xsl:when test="Width">
<xsl:value-of select="Width" />
</xsl:when>
<xsl:otherwise>1</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="Item" mode="item">
<xsl:value-of select="Display" />
</xsl:template>
</xsl:stylesheet>
EXPECTED RESULT*:
Scenario 1: Item 2 Width = 2
Scenario 2: Item 2 Width = 2, Item 4 Width = 2
*For illustration purposes, I used HTML table in my scenario screenshots, but basically the items need not to be precisely aligned by row because my elements may vary by height. The ultimate goal is to group them into columns.
Current Output:
My current XSLT produces a raw matrix which shows the column assignment of the element and its corresponding width. How do I convert this matrix to a layout of HTML <div> that will look similar to the expected results above?
My XSLT Fiddle: https://xsltfiddle.liberty-development.net/6pS26mX/1
Thank you!
I have a string like below and trying to convert into json format:
Test "out" and \new
Expected output is
Test \"out\" and \new
I tried by calling templates for escapequote - working fine for escape quotes:
<xsl:template name="escapeQuote">
<xsl:param name="pText" select="concat(normalize-space(.), '')" />
<xsl:if test="string-length($pText) >0">
<xsl:value-of select="substring-before(concat($pText, '"'), '"')" />
<xsl:if test="contains($pText, '"')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText" select="substring-after($pText, '"')" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
Template for escape backslash - working for only backslash:
<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:choose>
<xsl:when test="contains($str, '\')">
<xsl:value-of select="concat(substring-before($str, '\'), '\\' )"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, '\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
My question how to call both templates or merge, please help me
Here is an example of how you can combine the two template calls, so that the output from jsonescape is used as an input parameter to escapequote
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="input">
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText">
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="." />
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</xsl:template>
<xsl:template name="escapeQuote">
<xsl:param name="pText" select="concat(normalize-space(.), '')" />
<xsl:if test="string-length($pText) >0">
<xsl:value-of select="substring-before(concat($pText, '"'), '"')" />
<xsl:if test="contains($pText, '"')">
<xsl:text>\"</xsl:text>
<xsl:call-template name="escapeQuote">
<xsl:with-param name="pText" select="substring-after($pText, '"')" />
</xsl:call-template>
</xsl:if>
</xsl:if>
</xsl:template>
<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:choose>
<xsl:when test="contains($str, '\')">
<xsl:value-of select="concat(substring-before($str, '\'), '\\' )"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, '\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
So, given this an input:
<input>Test "out" and \new</input>
The following is output
Test \"out\" and \\new
Note that the order is important, because if you reversed the order of the template calls, the " would get converted to \" by the escapequote template, which would then get converted to \\" by the jsonescape template.
Alternatively, as both template do a similar thing, of putting a \ before specific characters, you could combine the two templates into one.
Try this XSLT too
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="text" />
<xsl:template match="input">
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="." />
</xsl:call-template>
</xsl:template>
<xsl:template name="jsonescape">
<xsl:param name="str" select="."/>
<xsl:param name="escapeChars" select="'\"'" />
<xsl:variable name="first" select="substring(translate($str, translate($str, $escapeChars, ''), ''), 1, 1)" />
<xsl:choose>
<xsl:when test="$first">
<xsl:value-of select="concat(substring-before($str, $first), '\', $first)"/>
<xsl:call-template name="jsonescape">
<xsl:with-param name="str" select="substring-after($str, $first)"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
I am trying to obtain the target xml such that value of a node is comma seperated values of sum of previous similar node values. For e.g:
Input:
<Items>
<Item>
<name>Drakshi</name>
<price>50</price>
</Item>
<Item>
<name>Godambi</name>
<price>30</price>
</Item>
<Item>
<name>Badami</name>
<price>70</price>
</Item>
</Items>
Output:
<result>
50,80,150
</result>
As you can see above it is 50, (50+30), (50+30+70)
I tried with for-each Item and was able to find the sum of only current node and previous selected node. Can you please guide me here
Try something like this:
<xsl:template match="Items">
<result>
<xsl:for-each select="Item">
<xsl:value-of select="sum(preceding-sibling::Item/price | price) " />
<xsl:if test="position() != last()">, </xsl:if>
</xsl:for-each>
</result>
</xsl:template>
While you could use sum(preceding-sibling::...) a more efficient solution would use a recursive template to accumulate the sum one value at a time:
<xsl:template match="/Items">
<result>
<xsl:call-template name="run-total">
<xsl:with-param name="values" select="Item/price"/>
</xsl:call-template>
</result>
</xsl:template>
<xsl:template name="run-total">
<xsl:param name="values"/>
<xsl:param name="i" select="1"/>
<xsl:param name="total" select="0"/>
<xsl:variable name="balance" select="$total + $values[$i]" />
<xsl:value-of select="$balance" />
<xsl:if test="$i < count($values)">
<xsl:text>,</xsl:text>
<xsl:call-template name="run-total">
<xsl:with-param name="values" select="$values"/>
<xsl:with-param name="i" select="$i + 1"/>
<xsl:with-param name="total" select="$balance"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
If you want to solve it with XSLT 2.0 then I don't think you need any user defined function, you can write a single XPath 2.0 expression:
<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:template match="Items">
<result>
<xsl:value-of
select="for $pos in 1 to count(Item)
return sum(subsequence(Item, 1, $pos)/price)"
separator=","/>
</result>
</xsl:template>
</xsl:stylesheet>
Thanks for the replies. I followed this approach which worked for me:
<xsl:function name="kk:getSumTillIndex">
<xsl:param name="index"/>
<xsl:variable name="Var_PriceArray" select="$Items/item[position()<=$index]//price/text()"/>
<xsl:for-each select="$Var_PriceArray">
<Item>
<xsl:value-of select="current()"/>
</Item>
</xsl:for-each>
</xsl:function>
<xsl:function name="kk:calulatePriceSequence">
<xsl:variable name="set" select="$Items/Item" />
<xsl:variable name="count" select="count($set)" />
<xsl:for-each select="$set">
<xsl:if test="position() <= $count">
<xsl:value-of select="sum(kk:getSumTillIndex(position()))"/>
<xsl:if test="position() < number($count)-1">
<xsl:value-of select="','" />
</xsl:if>
</xsl:if>
</xsl:for-each>
</xsl:function>