How to get maximum or minimum from multiple sets of calculated values with xpath - xslt

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

Related

How to declare a sequence in XSLT?

I need to declare a fixed sequence of numbers. How do I do this?
For example, is it (I'm guessing here):
<xsl:element name="xsl:param">
<xsl:attribute name="name">MySequence</xsl:attribute>
<xsl:sequence>(1,2,3,4)</xsl:sequence>
</xsl:element>
or
<xsl:element name="xsl:param">
<xsl:attribute name="name">MySequence</xsl:attribute>
<xsl:sequence>1,2,3,4</xsl:sequence>
</xsl:element>
or what?
Thanks
If you're using XSLT 2.0, you can just create the sequence directly in the select like:
<xsl:param name="MySequence" select="('1','2','3','4')"/>
XSLT based verification...
XSLT 2.0
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:strip-space elements="*"/>
<xsl:param name="seq" select="('23453','74365','98','653')"/>
<xsl:template match="/">
<xsl:for-each select="$seq">
<xsl:value-of select="concat('Item ',position(),': ',.,'
')"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
applied to any XML input produces:
Item 1: 23453
Item 2: 74365
Item 3: 98
Item 4: 653
To build a sequence in the XSLT 2.0 sense you use a select e.g.
<xsl:sequence select="1 to 4" />
But if you're adding the value to an element you may prefer value-of
<xsl:value-of select="1 to 4" separator="," />
Given the snippet in the question, this would generate output XML of
<xsl:param name="MySequence">1,2,3,4</xsl:param>
Which makes the value of the generated param a comma separated string. If you actually want the param value to be a sequence in the generated XSLT then you need to generate a select attribute instead of using element content
<xsl:element name="xsl:param">
<xsl:attribute name="name" select="'MySequence'"/>
<xsl:attribute name="select">
<xsl:text>(</xsl:text>
<xsl:value-of select="1 to 4" separator=","/>
<xsl:text>)</xsl:text>
</xsl:attribute>
</xsl:element>
Giving output of
<xsl:param name="MySequence" select="(1,2,3,4)" />

XSLT recursive sum troubles

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>`

Update text in an element - Effectively

I need help from experts here to optimize the solution of updating string value in an element . I have this xml file as an input...
<?xml version="1.0" encoding="UTF-8"?>
<FullRequest>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<RequestDetail>
<!-- Request Element is a string of fixed length (50 characters) -->
<Request>THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG AGAIN!</Request>
<Request>THE TIME FOX JUMPED OVER THE LAZY DOG, PROGRESSED!</Request>
<Request>OWING TO THE WIDESPREAD KNOWLEDGE OF THE PHRASE AN</Request>
</RequestDetail>
</FullRequest>
Offset 5 in Request element will be unique and can be cross-referenced. Q, T and G are the IDs in the above request.
<?xml version="1.0" encoding="UTF-8"?>
<FullResponse>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<ResponseDetail>
<!-- Response element repeats for 3 times as indicated by the value of Looptimes -->
<!-- Id has a unique value -->
<Response>
<Id>Q</Id>
<Value1>ABC</Value1>
<Value2>XYZ</Value2>
<Value3>FGK</Value3>
</Response>
<Response>
<Id>T</Id>
<Value1>123</Value1>
<Value2>YOK</Value2>
<Value3>DSL</Value3>
</Response>
<Response>
<Id>G</Id>
<Value1>BAT</Value1>
<Value2>TKR</Value2>
<Value3>LAF</Value3>
</Response>
</ResponseDetail>
</FullResponse>
Taking the above xml, offset positions 10, 15 and 20 need to be replaced with values Value1, Value2 and Value3 respectively.
I have this XSL which does the job. Not sure how good this solution will work with few thousand records of 5000 characters each (50 characters in the Request element shown as an example here) with about 20 locations to edit.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:regexp="http://exslt.org/regular-expressions" exclude-result-prefixes="regexp">
<xsl:output omit-xml-declaration="no" indent="yes"/>
<xsl:preserve-space elements="*"/>
<xsl:variable name="WebResponse" select="document('local:///ic1-data.xml')"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="/FullRequest/RequestDetail/Request">
<xsl:variable name="currentLine" select="."/>
<xsl:variable name="id" select="substring($currentLine,5,1)"/>
<xsl:for-each select="$WebResponse/FullResponse/ResponseDetail/Response">
<xsl:variable name="ResId" select="Id"/>
<xsl:if test="$id = $ResId">
<xsl:element name="{name()}">
<!-- Update Value1 -->
<xsl:variable name="from" select="substring($currentLine,10,3)"/>
<xsl:variable name="UpdatedValue1"
select="regexp:replace($currentLine,$from,'',Value1)"/>
<!-- Update Value2 -->
<xsl:variable name="from2" select="substring($UpdatedValue1,15,3)"/>
<xsl:variable name="UpdatedValue2"
select="regexp:replace($UpdatedValue1,$from2,'',Value2)"/>
<!-- Update Value3 -->
<xsl:variable name="from3" select="substring($UpdatedValue2,20,3)"/>
<xsl:value-of select="regexp:replace($UpdatedValue2,$from3,'',Value3)"/>
</xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The sample output will be as:
<?xml version="1.0" encoding="UTF-8"?>
<FullRequest>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<RequestDetail>
<Response>THE QUICKABCOWXYZOXFGKMPS OVER THE LAZY DOG AGAIN!</Response>
<Response>THE TIME 123 JYOKEDDSLER THE LAZY DOG, PROGRESSED!</Response>
<Response>OWING TO BAT WTKRSPLAFD KNOWLEDGE OF THE PHRASE AN</Response>
</RequestDetail>
</FullRequest>
I can only use XSLT 1.0
Can you suggest how to make this better?
Thanks.
Another more flexible approach would be:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:strip-space elements="*"/>
<xsl:key name="kResponseById" match="Response" use="Id"/>
<xsl:variable name="WebResponse" select="document('ic1-data.xml')"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Request/text()">
<xsl:variable name="vCurrent" select="."/>
<xsl:for-each select="$WebResponse">
<xsl:call-template name="replace">
<xsl:with-param name="pString" select="$vCurrent"/>
<xsl:with-param name="pValues"
select="key('kResponseById',
substring($vCurrent,5,1)
)/*[starts-with(local-name(),'Value')]"/>
</xsl:call-template>
</xsl:for-each>
</xsl:template>
<xsl:template name="replace">
<xsl:param name="pString"/>
<xsl:param name="pValues"/>
<xsl:choose>
<xsl:when test="$pValues">
<xsl:variable name="pLimit"
select="substring-after(
local-name($pValues[1]),
'Value'
) * 5 + 4"/>
<xsl:call-template name="replace">
<xsl:with-param name="pString"
select="concat(
substring($pString, 1, $pLimit),
$pValues[1],
substring($pString, $pLimit + 4)
)"/>
<xsl:with-param name="pValues"
select="$pValues[position()!=1]"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$pString"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Output:
<FullRequest>
<Header>
<Looptimes>3</Looptimes>
<SomeElement>blah!</SomeElement>
<AnotherElement>blah!!</AnotherElement>
</Header>
<RequestDetail>
<!-- Request Element is a string of fixed length (50 characters) -->
<Request>THE QUICKABCOWXYZOXFGKMPS OVER THE LAZY DOG AGAIN!</Request>
<Request>THE TIME 123 JYOKEDDSLER THE LAZY DOG, PROGRESSED!</Request>
<Request>OWING TO BAT WTKRSPLAFD KNOWLEDGE OF THE PHRASE AN</Request>
</RequestDetail>
</FullRequest>

Merge functionality of two xsl files into a single file (continued.....)

This is in continuation of my question:
Merge functionality of two xsl files into a single file (not a xsl import or include issue)
I have to merge the solution (xsl) of above question to below xsl:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format">
<xsl:template match="/">
<Declaration>
<Message>
<Meduim>
<xsl:value-of select="/Declaration/Message/Meduim"/>
</Meduim>
<MessageIdentifier>
<xsl:value-of select="/Declaration/Message/MessageIdentifier"/>
</MessageIdentifier>
<ControlingAgencyCode>
<xsl:value-of select="/Declaration/Message/ControlingAgencyCode"/>
</ControlingAgencyCode>
<AssociationAssignedCode>
<xsl:value-of select="/Declaration/Message/AssociationAssignedCode"/>
</AssociationAssignedCode>
<CommonAccessReference>
<xsl:value-of select="/Declaration/Message/CommonAccessReference"/>
</CommonAccessReference>
</Message>
<BeginingOfMessage>
<MessageCode>
<xsl:value-of select="/Declaration/BeginingOfMessage/MessageCode"/>
</MessageCode>
<DeclarationCurrency>
<xsl:value-of select="/Declaration/BeginingOfMessage/DeclarationCurrency"/>
</DeclarationCurrency>
<MessageFunction>
<xsl:value-of select="/Declaration/BeginingOfMessage/MessageFunction"/>
</MessageFunction>
</BeginingOfMessage>
<Header>
<ProcessingInformation>
<xsl:for-each select="/Declaration/Header/ProcessingInformation/ProcessingInstructions">
<ProcessingInstructions>
<xsl:value-of select="."/>
</ProcessingInstructions>
</xsl:for-each>
</ProcessingInformation>
<xsl:for-each select="/Declaration/Header/Seal">
<Seal>
<SealID>
<xsl:value-of select="SealID"/>
</SealID>
<SealLanguage>
<xsl:value-of select="SealLanguage"/>
</SealLanguage>
</Seal>
</xsl:for-each>
<xsl:choose>
<xsl:when test='/Declaration/Header/DeclarantsReference = ""'>
<DeclarantsReference>
<xsl:text disable-output-escaping="no">A</xsl:text>
</DeclarantsReference>
</xsl:when>
<xsl:otherwise>
<DeclarantsReference>
<xsl:value-of select="/Declaration/Header/DeclarantsReference"/>
</DeclarantsReference>
</xsl:otherwise>
</xsl:choose>
<xsl:for-each select="/Declaration/Header/Items">
<Items>
<CustomsStatusOfGoods>
<CPC>
<xsl:value-of select="CustomsStatusOfGoods/CPC"/>
</CPC>
<CommodityCode>
<xsl:value-of select="CustomsStatusOfGoods/CommodityCode"/>
</CommodityCode>
<ECSuplementaryMeasureCode1>
<xsl:value-of select="CustomsStatusOfGoods/ECSuplementaryMeasureCode1"/>
</ECSuplementaryMeasureCode1>
<ECSuplementaryMeasureCode2>
<xsl:value-of select="CustomsStatusOfGoods/ECSuplementaryMeasureCode2"/>
</ECSuplementaryMeasureCode2>
<PreferenceCode>
<xsl:value-of select="CustomsStatusOfGoods/PreferenceCode"/>
</PreferenceCode>
</CustomsStatusOfGoods>
<xsl:for-each select="ItemAI">
<ItemAI>
<AICode>
<xsl:value-of select="AICode"/>
</AICode>
<AIStatement>
<xsl:value-of select="AIStatement"/>
</AIStatement>
<AILanguage>
<xsl:value-of select="AILanguage"/>
</AILanguage>
</ItemAI>
</xsl:for-each>
<Locations>
<CountryOfOriginCode>
<xsl:value-of select="Locations/CountryOfOriginCode"/>
</CountryOfOriginCode>
<xsl:for-each select="Locations/ItemCountryonRouteCode">
<ItemCountryonRouteCode>
<xsl:value-of select="."/>
</ItemCountryonRouteCode>
</xsl:for-each>
<ItemDispatchCountry>
<xsl:value-of select="Locations/ItemDispatchCountry"/>
</ItemDispatchCountry>
<ItemDestinationCountry>
<xsl:value-of select="Locations/ItemDestinationCountry"/>
</ItemDestinationCountry>
</Locations>
<Measurements>
<GrossMass>
<xsl:value-of select="Measurements/GrossMass"/>
</GrossMass>
<NetMass>
<xsl:value-of select="Measurements/NetMass"/>
</NetMass>
<SupplementaryUnits>
<xsl:value-of select="Measurements/SupplementaryUnits"/>
</SupplementaryUnits>
<ThirdQuantity>
<xsl:value-of select="Measurements/ThirdQuantity"/>
</ThirdQuantity>
</Measurements>
<xsl:for-each select="Package">
<Package>
<PackageNumber>
<xsl:value-of select="PackageNumber"/>
</PackageNumber>
<PackageKind>
<xsl:value-of select="PackageKind"/>
</PackageKind>
<PackageMarks>
<xsl:value-of select="PackageMarks"/>
</PackageMarks>
<PackageLanguage>
<xsl:value-of select="PackageLanguage"/>
</PackageLanguage>
</Package>
</xsl:for-each>
<PriceValue>
<ItemStatisticalValue>
<xsl:value-of select="PriceValue/ItemStatisticalValue"/>
</ItemStatisticalValue>
<ItemPrice>
<xsl:value-of select="PriceValue/ItemPrice"/>
</ItemPrice>
</PriceValue>
<ItemReferences>
<xsl:for-each select="ItemReferences/ContainerID">
<ContainerID>
<xsl:value-of select="."/>
</ContainerID>
</xsl:for-each>
<QuotaNo>
<xsl:value-of select="ItemReferences/QuotaNo"/>
</QuotaNo>
<UNDangerousGoodsCode>
<xsl:value-of select="ItemReferences/UNDangerousGoodsCode"/>
</UNDangerousGoodsCode>
</ItemReferences>
<GoodsDescription>
<GoodsDescription>
<xsl:value-of select="GoodsDescription/GoodsDescription"/>
</GoodsDescription>
<GoodsDescriptionLanguage>
<xsl:value-of select="GoodsDescription/GoodsDescriptionLanguage"/>
</GoodsDescriptionLanguage>
</GoodsDescription>
<Documents>
<xsl:for-each select="Documents/PreviousDocument">
<PreviousDocument>
<PreviousDocumentKind>
<xsl:value-of select="PreviousDocumentKind"/>
</PreviousDocumentKind>
<PreviousDocumentIdentifier>
<xsl:value-of select="PreviousDocumentIdentifier"/>
</PreviousDocumentIdentifier>
<PreviousDocumentType>
<xsl:value-of select="PreviousDocumentType"/>
</PreviousDocumentType>
<PreviousDocumentLanguage>
<xsl:value-of select="PreviousDocumentLanguage"/>
</PreviousDocumentLanguage>
</PreviousDocument>
</xsl:for-each>
<xsl:for-each select="Documents/ItemDocument">
<ItemDocument>
<DocumentCode>
<xsl:value-of select="DocumentCode"/>
</DocumentCode>
<DocumentPart>
<xsl:value-of select="DocumentPart"/>
</DocumentPart>
<DocumentQuantity>
<xsl:value-of select="DocumentQuantity"/>
</DocumentQuantity>
<DocumentReason>
<xsl:value-of select="DocumentReason"/>
</DocumentReason>
<DocumentReference>
<xsl:value-of select="DocumentReference"/>
</DocumentReference>
<DocumentStatus>
<xsl:value-of select="DocumentStatus"/>
</DocumentStatus>
<DocumentLanguage>
<xsl:value-of select="DocumentLanguage"/>
</DocumentLanguage>
</ItemDocument>
</xsl:for-each>
</Documents>
<Valuation>
<ValuationMethodCode>
<xsl:value-of select="Valuation/ValuationMethodCode"/>
</ValuationMethodCode>
<ItemValuationAdjustmentCode>
<xsl:value-of select="Valuation/ItemValuationAdjustmentCode"/>
</ItemValuationAdjustmentCode>
<ItemValuationAdjustmentPercentage>
<xsl:value-of select="Valuation/ItemValuationAdjustmentPercentage"/>
</ItemValuationAdjustmentPercentage>
</Valuation>
<ItemTransportChargeMOP>
<xsl:value-of select="ItemTransportChargeMOP"/>
</ItemTransportChargeMOP>
<xsl:for-each select="ItemProcessingInstructions">
<ItemProcessingInstructions>
<xsl:value-of select="."/>
</ItemProcessingInstructions>
</xsl:for-each>
</Items>
</xsl:for-each>
<NumberOfPackages>
<xsl:value-of select="/Declaration/Header/NumberOfPackages"/>
</NumberOfPackages>
</Header>
</Declaration>
</xsl:template>
</xsl:stylesheet>
so for source xml
<Declaration>
<Message>
<Meduim>#+#</Meduim>
<MessageIdentifier>AA</MessageIdentifier>
<CommonAccessReference></CommonAccessReference>
</Message>
<BeginingOfMessage>
<MessageCode>ISD</MessageCode>
<DeclarationCurrency></DeclarationCurrency>
<MessageFunction>5</MessageFunction>
</BeginingOfMessage>
</Declaration>
the final output is
<Declaration>
<Message>
<Meduim></Meduim>
<MessageIdentifier>AA</MessageIdentifier>
</Message>
<BeginingOfMessage>
<MessageCode>ISD</MessageCode>
<MessageFunction>5</MessageFunction>
</BeginingOfMessage>
</Declaration>
I. Performing a chain of transformations is used quite often in XSLT applications, though doing this entirely in XSLT 1.0 requires the use of the vendor-specific xxx:node-set() function. In XSLT 2.0 no such extension is needed as the infamous RTF datatype is eliminated there.
Here is an example (too-simple to be meaningful, but illustrating completely how this is done):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:variable name="vrtfPass1">
<xsl:apply-templates select="/*/*"/>
</xsl:variable>
<xsl:variable name="vPass1"
select="ext:node-set($vrtfPass1)"/>
<xsl:apply-templates mode="pass2"
select="$vPass1/*"/>
</xsl:template>
<xsl:template match="num[. mod 2 = 1]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="num" mode="pass2">
<xsl:copy>
<xsl:value-of select=". *2"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
when this transformation is applied on the following XML document:
<nums>
<num>01</num>
<num>02</num>
<num>03</num>
<num>04</num>
<num>05</num>
<num>06</num>
<num>07</num>
<num>08</num>
<num>09</num>
<num>10</num>
</nums>
the wanted, correct result is produced:
<num>2</num>
<num>6</num>
<num>10</num>
<num>14</num>
<num>18</num>
Explanation:
In the first step the XML document is transformed and the result is defined as the value of the variable $vrtfPass1. This copies only the num elements that have odd value (not even).
The $vrtfPass1 variable, being of type RTF, is not directly usable for XPath expressions so we convert it to a normal tree, using the EXSLT (implemented by most XSLT 1.0 processors) function ext:node-set and defining another variable -- $vPass1 whose value is this tree.
We now perform the second transformation in our chain of transformations -- on the result of the first transformation, that is kept as the value of the variable $vPass1. Not to mess with the first-pass template, we specify that the new processing should be in a named mode, called "pass2". In this mode the value of any num element is multiplied by two.
See also the answer of Michael Kay to your first question, which also explained this general technique.
II. XSLT 2.0 solution (no RTFs):
<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="/">
<xsl:variable name="vPass1" >
<xsl:apply-templates select="/*/*"/>
</xsl:variable>
<xsl:apply-templates mode="pass2"
select="$vPass1/*"/>
</xsl:template>
<xsl:template match="num[. mod 2 = 1]">
<xsl:copy-of select="."/>
</xsl:template>
<xsl:template match="num" mode="pass2">
<xsl:copy>
<xsl:value-of select=". *2"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
III. Using the compose() and compose-flist() functions/templates of FXSL
The FXSL library provides two convenient functions/template that support easy chaining of transformations. The former composes two functions/transformations while the latter composes all functions/transformations that are provided in a sequence.
Here is a simple, complete code example:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:f="http://fxsl.sf.net/"
xmlns:myFun1="f:myFun1"
xmlns:myFun2="f:myFun2"
xmlns:ext="http://exslt.org/common"
exclude-result-prefixes="xsl f ext myFun1 myFun2"
>
<xsl:import href="compose.xsl"/>
<xsl:import href="compose-flist.xsl"/>
<!-- to be applied on any xml source -->
<xsl:output method="text"/>
<myFun1:myFun1/>
<myFun2:myFun2/>
<xsl:template match="/">
<xsl:variable name="vFun1" select="document('')/*/myFun1:*[1]"/>
<xsl:variable name="vFun2" select="document('')/*/myFun2:*[1]"/>
Compose:
(*3).(*2) 3 =
<xsl:call-template name="compose">
<xsl:with-param name="pFun1" select="$vFun1"/>
<xsl:with-param name="pFun2" select="$vFun2"/>
<xsl:with-param name="pArg1" select="3"/>
</xsl:call-template>
<xsl:variable name="vrtfParam">
<xsl:copy-of select="$vFun1"/>
<xsl:copy-of select="$vFun2"/>
<xsl:copy-of select="$vFun1"/>
</xsl:variable>
Multi Compose:
(*3).(*2).(*3) 2 =
<xsl:call-template name="compose-flist">
<xsl:with-param name="pFunList" select="ext:node-set($vrtfParam)/*"/>
<xsl:with-param name="pArg1" select="2"/>
</xsl:call-template>
</xsl:template>
<xsl:template match="myFun1:*" mode="f:FXSL">
<xsl:param name="pArg1"/>
<xsl:value-of select="3 * $pArg1"/>
</xsl:template>
<xsl:template match="myFun2:*" mode="f:FXSL">
<xsl:param name="pArg1"/>
<xsl:value-of select="2 * $pArg1"/>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on any XML document (not used), the wanted, correct results are produced:
Compose:
(*3).(*2) 3 =
18
Multi Compose:
(*3).(*2).(*3) 2 =
36
Pure XSLT 1.0 does not support chaining templates (nor stylesheets as a whole). You can either solve this program outside of XSLT by calling the second xslt template and passing it the output of the first manually, or you can use the fairly pervasive extension function node-set(). MSXML, .NET, EXSL and many other implementations support such a function. The namespace prefix for node-set varies depending on XSLT implementation, but the EXSL prefix is a good bet (and .NET, though undocumented, supports this).
To use node-set store the result of a template in an xsl:variable or xsl:param and do something like <xsl:apply-templates select="exsl:node-set($myvarname)"/>.
Finally, you can of course rewrite your two templates to provide the functionality of both in one pass - but in general, this isn't a trivial thing to do.
I don't understand the problem.
The solution I posted in your last question already works.
For this input:
<?xml version="1.0" encoding="UTF-8"?>
<Declaration>
<Message>
<Meduim>#+#</Meduim>
<MessageIdentifier>AA</MessageIdentifier>
<CommonAccessReference></CommonAccessReference>
</Message>
<BeginingOfMessage>
<MessageCode>ISD</MessageCode>
<DeclarationCurrency></DeclarationCurrency>
<MessageFunction>5</MessageFunction>
</BeginingOfMessage>
</Declaration>
Output will be:
<?xml version="1.0" encoding="UTF-8"?>
<Declaration>
<Message>
<Meduim/>
<MessageIdentifier>AA</MessageIdentifier>
</Message>
<BeginingOfMessage>
<MessageCode>ISD</MessageCode>
<MessageFunction>5</MessageFunction>
</BeginingOfMessage>
</Declaration>
You, in fact, don't need your original stylesheet, because basically you just copy the tree.

Xslt distinct select / Group by

<statisticItems>
<statisticItem id="1" frontendGroupId="2336" caseId="50264" />
<statisticItem id="2" frontendGroupId="2336" caseId="50264" />
<statisticItem id="3" frontendGroupId="2337" caseId="50265" />
<statisticItem id="4" frontendGroupId="2337" caseId="50266" />
<statisticItem id="5" frontendGroupId="2337" caseId="50266" />
</statisticItems>
<?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" omit-xml-declaration="yes"/>
<xsl:key name="statistic-by-frontendGroupId" match="statisticItem" use="#frontendGroupId" />
<xsl:for-each select="statisticItems/statisticItem[count(.|key('statistic-by-frontendGroupId', #frontendGroupId)[1]) = 1]">
<xsl:value-of select="#frontendGroupId"/>
</xsl:for-each>
What i have done so fare is to go through all distict frontendGroupIds. What i would like to do now is to do a count of all the distict caseIds for each distict frontendGroupId but i cant seem to make that work. Can someone help me here plz?
You were close:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" />
<xsl:key
name="statistic-by-frontendGroupId"
match="statisticItem"
use="#frontendGroupId"
/>
<xsl:template match="statisticItems">
<xsl:for-each select="
statisticItem[
count(
. | key('statistic-by-frontendGroupId', #frontendGroupId)[1]
) = 1
]
">
<xsl:value-of select="#frontendGroupId"/>
<xsl:value-of select="' - '"/>
<!-- simple: the item count is the node count of the key -->
<xsl:value-of select="
count(
key('statistic-by-frontendGroupId', #frontendGroupId)
)
"/>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
This results in:
2336 - 2
2337 - 3
EDIT - Oh, I see you want the distinct count within the group. This would be:
<!-- the other key from the above solution is still defined -->
<xsl:key
name="kStatisticItemByGroupAndCase"
match="statisticItem"
use="concat(#frontendGroupId, ',', #caseId)"
/>
<xsl:template match="statisticItems">
<xsl:for-each select="
statisticItem[
count(
. | key('kStatisticItemByGroup', #frontendGroupId)[1]
) = 1
]
">
<xsl:value-of select="#frontendGroupId"/>
<xsl:value-of select="' - '"/>
<xsl:value-of select="
count(
key('kStatisticItemByGroup', #frontendGroupId)[
count(
. | key('kStatisticItemByGroupAndCase', concat(#frontendGroupId, ',', #caseId))[1]
) = 1
]
)
"/>
<xsl:value-of select="'
'"/>
</xsl:for-each>
</xsl:template>
Which looks (admittedly) a bit frightening. It outputs:
2336 - 1
2337 - 2
The core expression:
count(
key('kStatisticItemByGroup', #frontendGroupId)[
count(
. | key('kStatisticItemByGroupAndCase', concat(#frontendGroupId, ',', #caseId))[1]
) = 1
]
)
boils down to:
Count the nodes from "key('kStatisticItemByGroup', #frontendGroupId)" that fulfill the following condition: They are the first in their respective "kStatisticItemByGroupAndCase" group.
Looking closely, you will find that this is no more complicated than what you already do. :-)
EDIT: One last hint. Personally, I find this a lot more expressive than the above expressions, because it emphasizes node equality a lot more than the "count(.|something) = 1" approach:
count(
key('kStatisticItemByGroup', #frontendGroupId)[
generate-id()
=
generate-id(
key('kStatisticItemByGroupAndCase', concat(#frontendGroupId, ',', #caseId))[1]
)
]
)
The result is the same.
You are attempting to sort via the dreaded 'MUENCHIAN' method - something i've found so confusing that is not worth trying - so i worked out my own method.
It uses two transformations instead of one.
The first sorts the data into the right order based on your grouping requirements -- your sample data is already in the right order so i'll leave it out of this explanation( ask if you want help here)
The second transformation does the grouping simply by comparing one node to the next:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="statisticItems">
<groupedItem>
<xsl:apply-templates select="statisticItem"></xsl:apply-templates>
</groupedItem>
</xsl:template>
<xsl:template match="statisticItem">
<xsl:choose>
<xsl:when test="position()=1">
<xsl:apply-templates select="#frontendGroupId" />
</xsl:when>
<xsl:when test="#frontendGroupId!=preceding-sibling::statisticItem[1]/#frontendGroupId">
<xsl:apply-templates select="#frontendGroupId" />
</xsl:when>
</xsl:choose>
<xsl:apply-templates select="#caseId" />
</xsl:template>
<xsl:template match="#frontendGroupId">
<group>
<xsl:variable name="id" select="." ></xsl:variable>
<xsl:attribute name="count">
<xsl:value-of select="count(//statisticItem/#frontendGroupId[.=$id])"/>
</xsl:attribute>
<xsl:value-of select="." />
</group>
</xsl:template>
<xsl:template match="#caseId">
<value>
<xsl:value-of select="." />
</value>
</xsl:template>
</xsl:stylesheet>
With this method you can go several groups deep and still have maintainable code.