I want to ask. there is posible to add condition which will be checking xml data with lookup table, and if we didnt't have value in lookup table add const 8 to output?
xslt Code:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="Department" match="Department" use="../Collection"/>
<xsl:template match="/">
<document>
<xsl:apply-templates/>
</document>
</xsl:template>
<xsl:template match="line">
<xsl:variable name="inputDep" select="field[#id='3']"/>
<Department>
<xsl:for-each select="document('lookup.xml')">
<xsl:for-each select="key('Deparment',$inputDep)">
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:for-each>
</Department>
</xsl:template>
</xsl:stylesheet>
lookup table:
<document>
<line-item>
<Collection>1</Collection>
<Department>3</Department>
</line-item>
<line-item>
<Collection>2</Collection>
<Department>1</Department>
</line-item>
<line-item>
<Collection>3</Collection>
<Department>2</Department>
</line-item>
</document>
xml file:
<document>
<line id="0">
<field id="3"><![CDATA[1]]></field>
</line>
<line id="1">
<field id="3"/>
</line>
<line id="2">
<field id="3"/><![CDATA[4]]></field>
</line>
</document>
result:
<Department>3<Department>
<Department>8<Department>
<Department>8<Department>
You could assign the looked-up value to a variable and choose what to output based on whether anything was found.
Edit 2: A full demonstration stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="Department" match="Department" use="../Collection"/>
<xsl:template match="/">
<document>
<xsl:apply-templates/>
</document>
</xsl:template>
<xsl:template match="line">
<xsl:variable name="inputDep" select="field[#id='3']"/>
<Department>
<xsl:for-each select="document('lookup.xml')">
<xsl:variable name="value" select="key('Department',$inputDep)"/>
<xsl:choose>
<xsl:when test="$value">
<xsl:value-of select="$value"/> <!-- see note -->
</xsl:when>
<xsl:otherwise>8</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</Department>
</xsl:template>
</xsl:stylesheet>
Note: Replaced the xsl:for-each loop in the original stylesheet with a simple xsl:value-of, assuming that the looping the values was not intentional. If it actually was, you can replace this with a for-each loop.
Related
I have the following xml
<TopLevel>
<data m="R263">
<s ut="263firstrecord" lt="2013-02-16T09:21:40.393" />
<s ut="263secondrecord" lt="2013-02-16T09:21:40.393" />
</data>
<data m="R262">
<s ut="262firstrecord" lt="2013-02-16T09:21:40.393" />
<s ut="262secondrecord" lt="2013-02-16T09:21:40.393" />
</data>
</TopLevel>
I have some XSLT that does the call template but it's not itterating correctly.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:value-of select="#m" />
<xsl:variable name="vYourName" select="#m"/>
<xsl:choose>
<xsl:when test="#m='R262'">
<xsl:call-template name="R262"/>
</xsl:when>
</xsl:choose>
<xsl:choose>
<xsl:when test="#m='R263'">
<xsl:call-template name="R263"/>
</xsl:when>
</xsl:choose>
</xsl:template>
<xsl:template name="R262">
<xsl:for-each select="/TopLevel/data/s">
Column1=<xsl:value-of select="#ut" />
Column2=<xsl:value-of select="#lt" />
</xsl:for-each>
</xsl:template>
<xsl:template name="R263">
<xsl:for-each select="/TopLevel/data/s">
Column1=<xsl:value-of select="#ut" />
Column2=<xsl:value-of select="#lt" />
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This gives me 8 records insead of the 4 (<s> level) records. I know it has to do with my iteration ... but I am not sure how to address this.
I am also aware of the apply stylesheets but I couldn't unravel that mystery either ... If someone can help me with XSLT that will only process everything from <TopLevel> to <\TopLevel> checking the value of m at the <data> level and applying the stylesheet at the <s> level for each <s> record I will be greateful beyond belief.
I don't know what output you want to produce, but I suspect you want to replace
<xsl:for-each select="/TopLevel/data/s">
by
<xsl:for-each select="s">
that is, you only want to process the "s" elements within the "data" you are currently processing, rather than selecting all the "s" elements in the whole document.
Why not do this using apply-templates?
<xsl:template match="data">
...
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="s[../#m='R262']">
...
</xsl:template>
<xsl:template match="s[../#m='R263']">
...
</xsl:template>
If you want to use match template and apply-templates you could do the following which gives you also a text output just like your stylesheet does. So this XSLT applied to your original source XML:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="data">
<xsl:value-of select="#m"/>
<xsl:apply-templates select="s"/>
</xsl:template>
<xsl:template match="s">
Column1=<xsl:value-of select="#ut"/>
Column2=<xsl:value-of select="#lt"/>
</xsl:template>
</xsl:stylesheet>
gives you this output:
<?xml version="1.0" encoding="UTF-8"?>
R263
Column1=263firstrecord
Column2=2013-02-16T09:21:40.393
Column1=263secondrecord
Column2=2013-02-16T09:21:40.393
R262
Column1=262firstrecord
Column2=2013-02-16T09:21:40.393
Column1=262secondrecord
Column2=2013-02-16T09:21:40.393
You basically only match on the s and give out the attributes "ut" and "lt". You can also output XML which would look better.
Using this XSLT:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<root>
<xsl:apply-templates/>
</root>
</xsl:template>
<xsl:template match="data">
<list>
<xsl:apply-templates select="s"/>
</list>
</xsl:template>
<xsl:template match="s">
<xsl:element name="record">
<xsl:attribute name="m">
<xsl:value-of select="parent::data/#m"/>
</xsl:attribute>
<item>Column1=<xsl:value-of select="#ut"/></item>
<item>Column2=<xsl:value-of select="#lt"/></item>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
will give you this nice XML output:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<list>
<record m="R263">
<item>Column1=263firstrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
<record m="R263">
<item>Column1=263secondrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
</list>
<list>
<record m="R262">
<item>Column1=262firstrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
<record m="R262">
<item>Column1=262secondrecord</item>
<item>Column2=2013-02-16T09:21:40.393</item>
</record>
</list>
You have to adapt the original XSLT a little bit to get a nice XML structure. Also when matching s you "climb" up to element data to get the R-numbers for your attribute values.
The template matching root you need for a proper XML root element. <list> you could also get rid off then you have <record> as child of <root>.
Halo,
I have this xml document:
<document>
<Line>
<Line-Item>
<ID>5</ID>
<Quantity>100</Quantity>
</Line-Item>
</Line>
<Line>
<Line-Item>
<ID>6</ID>
<Quantity>9</Quantity>
</Line-Item>
</Line>
<Line>
<Line-Item>
<ID>60</ID>
<Quantity>3020</Quantity>
</Line-Item>
</Line>
</document>
And lookup file with table:
<lookup>
<Code>
<LookupID>5</LookupID>
<LookupQuantity>25</LookupQuantity>
</Code>
<Code>
<LookupID>6</LookupID>
<LookupQuantity>3</LookupQuantity>
</Code>
<Code>
<LookupID>70</LookupID>
<LookupQuantity>3</LookupQuantity>
</Code>
</lookup>
I should check lookup tables field lookup/Code/LookupId with document Line/Line-Item/ID. if lookup/Code/LookupId=document/Line/Line-Item/ID then document/Line/Line-Item/Quantity=document/Line/Line-Item/Quantity div lookup/Code/LookupQuantity, otherwise document/Line/Line-Item/Quantity=document/Line/Line-Item/Quantity
Needed result:
<document>
<Line>
<Line-Item>
<ID>5</ID>
<Quantity>4</Quantity>
</Line-Item>
</Line>
<Line>
<Line-Item>
<ID>6</ID>
<Quantity>3</Quantity>
</Line-Item>
</Line>
<Line>
<Line-Item>
<ID>60</ID>
<Quantity>3020</Quantity>
</Line-Item>
</Line>
</document>
My xslt:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:date="http://exslt.org/dates-and-times" extension-element-prefixes="date">
<xsl:output method="xml" encoding="UTF-8" indent="yes"/>
<xsl:key name="skRez" match="LookupQuantity" use="../LookupID"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Line/Line-Item/Quantity">
<xsl:variable name="inputS" select="..//ID"/>
<xsl:variable name="inputQ" select="..//Quantity"/>
<OrderedQuantity>
<xsl:for-each select="document('lookup.xml')">
<xsl:for-each select="key('skRez',$inputS)">
<xsl:variable name="Quantity" select="."/>
<xsl:choose>
<xsl:when test="$Quantity"><xsl:value-of select="ceiling($inputQ div $Quantity)"/></xsl:when>
<xsl:otherwise><xsl:value-of select="$inputQ"/></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:for-each>
</OrderedQuantity>
</xsl:template>
</xsl:stylesheet>
To do this, you can define a variable to hold the look-up data
<xsl:variable name="lookup" select="document('Lookup.xml')/lookup"/>
And then you can look-up the quantity for a particular Line-Item like so (In this case, the XSLT is currently positioned on a Quantity element within a Line-Item)
<xsl:variable name="quantity"
select="$lookup//Code[LookupID = current()/../ID]/LookupQuantity"/>
If nothing was returned by this variable, then you know the element was not in the look-up
Here is the full XSLT
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="lookup" select="document('Lookup.xml')/lookup"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Line-Item/Quantity">
<xsl:variable name="quantity"
select="$lookup//Code[LookupID = current()/../ID]/LookupQuantity"/>
<Quantity>
<xsl:choose>
<xsl:when test="number($quantity) = number($quantity)">
<xsl:value-of select="number(.) div number($quantity)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="."/>
</xsl:otherwise>
</xsl:choose>
</Quantity>
</xsl:template>
</xsl:stylesheet>
When applied to your sample XML, the following is output
<document>
<Line>
<Line-Item>
<ID>5</ID>
<Quantity>4</Quantity>
</Line-Item>
</Line>
<Line>
<Line-Item>
<ID>6</ID>
<Quantity>3</Quantity>
</Line-Item>
</Line>
<Line>
<Line-Item>
<ID>60</ID>
<Quantity>3020</Quantity>
</Line-Item>
</Line>
</document>
Please help with this xml:
<document>
<sheet id="0" name="Sheet1">
<line id="0">
<field id="0"><![CDATA[Calculate]]></field>
</line>
<line id="1">
<field id="0"><![CDATA[Quantity]]></field>
<field id="1"><![CDATA[Value]]></field>
</line>
<line id="2">
<field id="0"><![CDATA[3]]></field>
<field id="1"><![CDATA[2]]></field>
</line>
<line id="3">
<field id="0"><![CDATA[2]]></field>
<field id="1"><![CDATA[7]]></field>
</line>
<line id="4">
<field id="0"></field>
<field id="1"></field>
</line>
</sheet>
</document>
I need to get Sum of field[#id=1] before multiply this field with field[#id=0]. Line[#id=4] is empty so there should be condition to eliminate this line.
correct result should be:
<document>
<id>Calculate</id>
<line>
<sum>20</sum>
</line>
</document>
Here is an XSLT 2.0 solution that can be run with XSLT 2.0 processors like Saxon 9, XQSharp, AltovaXML:
<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="document">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="sheet">
<id><xsl:value-of select="line[#id = 0]/field"/></id>
<line>
<sum>
<xsl:value-of select="sum(line[field[#id = 1] castable as xs:double and field[#id = 0] castable as xs:double]/(field[#id = 1] * field[#id = 0]))"/>
</sum>
</line>
</xsl:template>
</xsl:stylesheet>
[edit] I am adding an XSLT 1.0 stylesheet with a named recursive template below:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="1.0">
<xsl:output indent="yes"/>
<xsl:template match="document">
<xsl:copy>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template name="sum">
<xsl:param name="lines"/>
<xsl:param name="total" select="0"/>
<xsl:choose>
<xsl:when test="not($lines)">
<xsl:value-of select="$total"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="sum">
<xsl:with-param name="lines" select="$lines[position() > 1]"/>
<xsl:with-param name="total" select="$total + $lines[1]/field[#id = 1] * $lines[1]/field[#id = 0]"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="sheet">
<id><xsl:value-of select="line[#id = 0]/field"/></id>
<line>
<sum>
<xsl:call-template name="sum">
<xsl:with-param name="lines"
select="line[number(field[#id = 1]) = number(field[#id = 1])
and number(field[#id = 0]) = number(field[#id = 0])]"/>
</xsl:call-template>
</sum>
</line>
</xsl:template>
</xsl:stylesheet>
I want to ask how to get total amount if we use substring function,
for example I have this xml,
<document>
<line id="0">
<field id="0"><![CDATA[AAAddd17aaass]]></field>
</line>
<line id="1">
<field id="0"><![CDATA[DDDaaa33sssaa]]></field>
</line>
</document>
I should sum(substring(field[#id='0'], 7,2)). Then I try to do that, I get this error message: Current Item is 'NaN of type xs:string. (I try to use number function but it doesn't help)
Please advice me how to solve it.
Easy in XSLT 2.0:
sum(field[#id='0']/number(substring(., 7,2)))
Rather more difficult in XSLT 1.0: see for example http://www.velocityreviews.com/forums/t170401-sum-over-computed-value.html
In XSLT 1.0 if you don't want to write recursive templates yourself, you can use the transform-and-sum template from FXSL.
See how to use it here.
Here is the full transformation:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:func-transform="f:func-transform"
exclude-result-prefixes="xsl f func-transform"
>
<xsl:import href="transform-and-sum.xsl"/>
<!-- to be applied on testTransform-and-sum.xml -->
<xsl:output method="text"/>
<func-transform:func-transform/>
<xsl:template match="/">
<xsl:call-template name="transform-and-sum">
<xsl:with-param name="pFuncTransform"
select="document('')/*/func-transform:*[1]"/>
<xsl:with-param name="pList" select="/*/*/*"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="func-transform:*" mode="f:FXSL">
<xsl:param name="arg1" select="0"/>
<xsl:value-of select="number(substring($arg1, 7,2))"/>
</xsl:template>
</xsl:stylesheet>
when applied on the provided XML document:
<document>
<line id="0">
<field id="0"><![CDATA[AAAddd17aaass]]></field>
</line>
<line id="1">
<field id="0"><![CDATA[DDDaaa33sssaa]]></field>
</line>
</document>
the wanted, correct result is produced:
50
I have to transform xml file where i should check field id '0', field id '1' and sum field id '2'. For example I have:
<document>
<line id="0">
<field id="0">MAR</field>
<field id="1">doc1</field>
<field id="2">2</field>
</line>
<line id="1">
<field id="0">MAR</field>
<field id="1">doc2</field>
<field id="2">3</field>
</line>
<line id="2">
<field id="0">AAA></field>
<field id="1">doc4</field>
</line>
<line id="3">
<field id="0">MAR</field>
<field id="1">doc1</field>
<field id="2">4</field>
</line>
</document>
result should be:
<type-MAR>
<document>doc1</document>
<sum>6</sum>
</type-MAR>
<type-MAR>
<document>doc2</document>
<sum>3</sum>
</type-MAR>
there I should take all MAR lines, and show some results which are depends of field id '1'.
My idea was, first off all do cycle(for each) and use condition(when). Maybe somebody offer more omptimal decision.
I add new note, how to check if data comes like that:
<field id="0">MAR999</field>
<field id="1">doc1-1231</field>
First field i try to use function contains 'MAR', others substring-before '-'. but I stuck when I try it use on Yours program. maybe you can take some advice for it?
This transformation:
<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:key name="kLineById0Id1" match="line[field[#id=2]]"
use="concat(field[#id=0],'+',field[#id=1])"/>
<xsl:template match=
"line[field[#id=2]
and
generate-id()
=
generate-id(key('kLineById0Id1',
concat(field[#id=0],
'+',field[#id=1])
)[1])
]
">
<xsl:element name="type-{field[#id=0]}">
<document>
<xsl:value-of select="field[#id=1]"/>
</document>
<sum>
<xsl:value-of select=
"sum(key('kLineById0Id1',
concat(field[#id=0],
'+',field[#id=1])
)
/field[#id=2]
)
"/>
</sum>
</xsl:element>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided XML document:
<document>
<line id="0">
<field id="0">MAR</field>
<field id="1">doc1</field>
<field id="2">2</field>
</line>
<line id="1">
<field id="0">MAR</field>
<field id="1">doc2</field>
<field id="2">3</field>
</line>
<line id="2">
<field id="0">AAA></field>
<field id="1">doc4</field>
</line>
<line id="3">
<field id="0">MAR</field>
<field id="1">doc1</field>
<field id="2">4</field>
</line>
</document>
produces the wanted, correct result:
<type-MAR>
<document>doc1</document>
<sum>6</sum>
</type-MAR>
<type-MAR>
<document>doc2</document>
<sum>3</sum>
</type-MAR>
Explanation: The Muenchian method for grouping is used with the key defined as the concatenation of two elements.
<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:key name="kLine" match="line" use="field[#id='1']"/>
<xsl:template match="/*">
<r>
<xsl:apply-templates select="line
[field[#id='0'] = 'MAR']
[count(
. | key('kLine', field[#id='1'])[1]
) = 1]
"/>
</r>
</xsl:template>
<xsl:template match="line">
<type-MAR>
<document>
<xsl:value-of select="field[#id='1']"/>
</document>
<sum>
<xsl:value-of select="
sum(
key('kLine', field[#id='1'])/
field[#id='2']
)"/>
</sum>
</type-MAR>
</xsl:template>
</xsl:stylesheet>
Correct against your sample will be:
<r>
<type-MAR>
<document>doc1</document>
<sum>6</sum>
</type-MAR>
<type-MAR>
<document>doc2</document>
<sum>3</sum>
</type-MAR>
</r>
This stylesheet:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:key name="kLineById0-Id1" match="line"
use="concat(field[#id='0'],'+',field[#id='1'])"/>
<xsl:param name="pId0" select="'MAR'"/>
<xsl:template match="document">
<result>
<xsl:apply-templates select="line[generate-id()=
generate-id(
key('kLineById0-Id1',
concat($pId0,
'+',
field[#id='1']
)
)[1]
)]"/>
</result>
</xsl:template>
<xsl:template match="line">
<xsl:element name="type-{$pId0}">
<document>
<xsl:value-of select="field[#id='1']"/>
</document>
<sum>
<xsl:value-of select="sum(key('kLineById0-Id1',
concat(field[#id='0'],
'+',
field[#id='1']
)
)/field[#id='2']
)"/>
</sum>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Output:
<result>
<type-MAR>
<document>doc1</document>
<sum>6</sum>
</type-MAR>
<type-MAR>
<document>doc2</document>
<sum>3</sum>
</type-MAR>
</result>
Note: Grouping by both #id attributes, sum group, dynamic element name, parameterized first #id.
Thanks for answers, I use Flack decision and make some correction:
<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:key name="kLine" match="line" use="substring(field[#id='1'],1,4)"/>
<xsl:template match="/*">
<document>
<xsl:apply-templates select="line[contains(field[#id='0'], 'MAR')][count(. | key('kLine', substring(field[#id='1'],1,4))[1]) = 1]"/>
</document>
</xsl:template>
<xsl:template match="line">
<type-MAR>
<document>
<xsl:value-of select="substring(field[#id='1'],1,4)"/>
</document>
<sum>
<xsl:value-of select="sum(key('kLine', substring(field[#id='1'],1,4))/field[#id='2'])"/>
</sum>
</type-MAR>
</xsl:template>
</xsl:stylesheet>
Dimitre and Alejandro decisions are also good and useful(maybe more professional). But Dimitre more concentrate on my task which I wrote, for example he use condition to check if we have the second field(I didn't wrote that not only MAR could have field2). Alejandro to check it use parameter, so for me it was a good lesson to search more information how to use it, because with xsl language I have less than one month experience. So for me was difficult to prepair Yours programs for my work. Flack text was more understandable for me as a beginner.