XSLT recursive sum troubles - xslt

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

Related

XSLT 1.0 to retrieve or parse a specific value

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>

Remove parent based on child value?

I want to display only those orders which dont have OrderLineSource = YTR. All other should be displayed....
My Sample XML :
<Orders>
<Order>
<OrderID>34209649</OrderID>
<OrderStatus>checkout_complete</OrderStatus>
<Amount>32.93</Amount>
<OrderCreation>2014-02-08T00:00:03.00</OrderCreation>
<OrderCompletion>2014-02-08T00:00:03.00</OrderCompletion>
<CustomerGUID>303965683</CustomerGUID>
<CSMPurchaserGUID>0</CSMPurchaserGUID>
<Brand>TRFE</Brand>
<SourceECommerceSystem>Framework</SourceECommerceSystem>
<Currency>GBP</Currency>
<OrderChannel>Online</OrderChannel>
<TransactionSummary>
<TransactionID>2407065</TransactionID>
<MerchantReference>TEdV-5648-34209649</MerchantReference>
<CardCategory>Personal</CardCategory>
<CardScheme>VISA Debit</CardScheme>
<CardCountry>gbr</CardCountry>
<CardIssuer>sdfsdf sdf Bank asdf</CardIssuer>
<CardStartDate>0/0</CardStartDate>
<CardExpiryDate>2016/08</CardExpiryDate>
<Amount>32.93</Amount>
<Currency>GBP</Currency>
<CardPAN>************4585</CardPAN>
<Created>2014-02-07T23:56:48</Created>
<Updated>2014-02-08T00:00:03</Updated>
<ResponseStatusCode>1</ResponseStatusCode>
<ResponseStatusReason>FULFILLED OK</ResponseStatusReason>
<HostedPageIdentifier>dsfasdf-ee85-4afa-bb6a-0afc6dc99896</HostedPageIdentifier>
<HostedPageURL>https://hps.datacash.com/hps/</HostedPageURL>
<PaymentStatus>Paid</PaymentStatus>
<PaymentType>Debit Card</PaymentType>
<NameOnCard>Miss L J adsf</NameOnCard>
<DataCashRef>56456456454</DataCashRef>
<MerchantID>545646</MerchantID>
<ThreeDCard>1</ThreeDCard>
<ThreeDRequested>1</ThreeDRequested>
<IPAddress>127.89.560.1</IPAddress>
</TransactionSummary>
<OrderLine>
<OrderLineID>84598837</OrderLineID>
<OrderID>34209649</OrderID>
<OrderLineLabel>GAREGSBV</OrderLineLabel>
<OrderLineSource>GHR</OrderLineSource>
<Quantity>1</Quantity>
<UnitPrice>32.93</UnitPrice>
<Total>32.93</Total>
<SKUCode>P0032</SKUCode>
<Title>Miss.</Title>
<FirstName>ertwer</FirstName>
<FamilyName>sdaf</FamilyName>
<DateOfBirth>1984-05-30</DateOfBirth>
<Email>sdfasdfa#hotmail.com</Email>
<Mobile>645646454</Mobile>
<PostChannel>0</PostChannel>
<TelephoneChannel>0</TelephoneChannel>
<EmailChannel>0</EmailChannel>
<TextAndOtherChannel>0</TextAndOtherChannel>
<BuildingNumber>27</BuildingNumber>
<AddressLine1>27</AddressLine1>
<AddressLine2>dsfasdf Road</AddressLine2>
<Town>London</Town>
<Country>sdfasdf er</Country>
<Postcode>KL7 2NS</Postcode>
<AddressValidated>1</AddressValidated>
<HKPolicy>
<PolicyNum>PP01754397</PolicyNum>
<ProductDescription>sadfsadfasdfgasdg</ProductDescription>
<CoverTypeDesc>Individual</CoverTypeDesc>
<SingleParentFamilyFlag>0</SingleParentFamilyFlag>
<PolicyTypeRefID>S</PolicyTypeRefID>
<PolicyTypeDesc>Sinasdfnce</PolicyTypeDesc>
<TierDesc>Classic</TierDesc>
<DestinationDesc>Worldwide including USA, Canada, Caribbean</DestinationDesc>
<TotalTravellers>1</TotalTravellers>
<NumOfAdults>1</NumOfAdults>
<NumOfUnder18>0</NumOfUnder18>
<PolicyStartDate>2014-02-08</PolicyStartDate>
<PolicyEndDate>2014-02-12</PolicyEndDate>
<BaseCost>32.93</BaseCost>
<Commission>11.18</Commission>
<UpsoldInd>0</UpsoldInd>
<TierRefID>C</TierRefID>
<DestinationRefID>W2</DestinationRefID>
<CoverTypeRefID>I</CoverTypeRefID>
<AONToPostPolicy>yes</AONToPostPolicy>
<SalesChannel>0011002</SalesChannel>
<WhereYouHeardOfUs>Press advertising</WhereYouHeardOfUs>
<TIPOLTraveller>
<TravellerUUID>1864-1</TravellerUUID>
<PolicyNum>PI0e31754397</PolicyNum>
<Title>Miss</Title>
<FirstName>sdfsf</FirstName>
<FamilyName>sdfsdf</FamilyName>
<DateOfBirth>1984-05-30</DateOfBirth>
<AgeBand>1864</AgeBand>
<DependentFlag>0</DependentFlag>
</TIPOLTraveller>
</TIPOLPolicy>
</OrderLine>
<OrderCustomerDetails>
<Title nil="true" />
<FirstName nil="true" />
<SecondName nil="true" />
<FamilyName nil="true" />
<DateOfBirth nil="true" />
<Email nil="true" />
<Telephone nil="true" />
<Mobile nil="true" />
<Gender nil="true" />
<PostChannel nil="true" />
<TelephoneChannel nil="true" />
<EmailChannel nil="true" />
<TextAndOtherChannel nil="true" />
<BuildingNumber>27</BuildingNumber>
<AddressLine1>27</AddressLine1>
<AddressLine2>asdfa Road</AddressLine2>
<Town>asdfasdf</Town>
<Country>United dsf</Country>
<Postcode>KH9 2NS</Postcode>
<AddressValidated>1</AddressValidated>
</OrderCustomerDetails>
</Order>
<Order>
<OrderID>34209674</OrderID>
<OrderStatus>checkout_complete</OrderStatus>
<Amount>11.13</Amount>
<OrderCreation>2014-02-08T00:08:40.00</OrderCreation>
<OrderCompletion>2014-02-08T00:08:40.00</OrderCompletion>
<CustomerGUID>303965688</CustomerGUID>
<CSMPurchaserGUID>0</CSMPurchaserGUID>
<Brand>TRFDS</Brand>
<SourceECommerceSystem>Framework</SourceECommerceSystem>
<Currency>GBP</Currency>
<OrderChannel>Online</OrderChannel>
<TransactionSummary>
<TransactionID>8115032</TransactionID>
<MerchantReference>JHF-0800-34209674</MerchantReference>
<CardCategory>Personal</CardCategory>
<CardScheme>VISA Debit</CardScheme>
<CardCountry>gbr</CardCountry>
<CardIssuer>Unknown</CardIssuer>
<CardStartDate>0/0</CardStartDate>
<CardExpiryDate>2016/09</CardExpiryDate>
<Amount>11.13</Amount>
<Currency>GBP</Currency>
<CardPAN>************4849</CardPAN>
<Created>2014-02-08T00:08:00</Created>
<Updated>2014-02-08T00:08:40</Updated>
<ResponseStatusCode>1</ResponseStatusCode>
<ResponseStatusReason>FULFILLED OK</ResponseStatusReason>
<HostedPageIdentifier>f3306487-d6ea-4200-9eea-99b1d6832a2e</HostedPageIdentifier>
<HostedPageURL>https://hps.dat.com/hps/</HostedPageURL>
<PaymentStatus>Paid</PaymentStatus>
<PaymentType>Debit Card</PaymentType>
<NameOnCard>Miss Jor </NameOnCard>
<DataCashRef>380010093738013</DataCashRef>
<MerchantID>21877049</MerchantID>
<ThreeDCard>1</ThreeDCard>
<ThreeDRequested>1</ThreeDRequested>
<IPAddress>86..25640.99</IPAddress>
</TransactionSummary>
<OrderLine>
<OrderLineID>84598874</OrderLineID>
<OrderID>34209674</OrderID>
<OrderLineLabel>3-1008617753325</OrderLineLabel>
<OrderLineSource>YTR</OrderLineSource>
<Quantity>1</Quantity>
<UnitPrice>11.13</UnitPrice>
<Total>11.13</Total>
<Title>Miss.</Title>
<FirstName>Jordan</FirstName>
<SecondName>oirut</SecondName>
<FamilyName>dfgsdfgs</FamilyName>
<Email>dfgsdfg#hotmail.com</Email>
<Mobile>654756464</Mobile>
<PostChannel>0</PostChannel>
<TelephoneChannel>0</TelephoneChannel>
<EmailChannel>0</EmailChannel>
<TextAndOtherChannel>0</TextAndOtherChannel>
<BuildingNumber>12</BuildingNumber>
<AddressLine1>12</AddressLine1>
<AddressLine2>sfgsdfg End Gardens</AddressLine2>
<Town>HEMEL sfgaefa</Town>
<Country>adf dgfsdfg</Country>
<Postcode>HP1 1SN</Postcode>
<OrderLineDetail>
<NameValuePair>
<Name>dfgsdfg</Name>
<Value>628</Value>
</NameValuePair>
<NameValuePair>
<Name>NameOnCard</Name>
<Value>adsfgasdgf Piper</Value>
</NameValuePair>
<NameValuePair>
<Name>DateOnCard</Name>
<Value>2014-02-05</Value>
</NameValuePair>
<NameValuePair>
<Name>CustomsOrSurcharge</Name>
<Value>CUSTOMS CHARGE TO PAY</Value>
</NameValuePair>
</OrderLineDetail>
</OrderLine>
<OrderCustomerDetails>
<Title>Miss.</Title>
<FirstName>Jordan</FirstName>
<SecondName>asdgfasdgf</SecondName>
<FamilyName nil="true" />
<DateOfBirth />
<Email>adfadf#hotmail.com</Email>
<Telephone />
<Mobile>adfasdf</Mobile>
<Gender nil="true" />
<PostChannel nil="true" />
<TelephoneChannel nil="true" />
<EmailChannel nil="true" />
<TextAndOtherChannel nil="true" />
<BuildingNumber>12</BuildingNumber>
<AddressLine1>12</AddressLine1>
<AddressLine2>adfasdf End Gardens</AddressLine2>
<Town>adsfasdf HEMPSTEAD</Town>
<Country>United asdfasdf</Country>
<Postcode>asd 1SN</Postcode>
</OrderCustomerDetails>
</Order>
</Orders>
I tried using XSLT :
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<!-- Orders -->
<xsl:template match="/*">
<xsl:element name="Orders">
<xsl:apply-templates select="./Order" />
</xsl:element>
</xsl:template>
<!-- Orders > Order -->
<xsl:template match="/Order">
<xsl:variable name="IsValid">
<xsl:call-template name="HasOrIsValidPOLine" />
</xsl:variable>
<xsl:if test="$IsValid='VALID'"> <!-- only display the order if there's a valid line under it-->
<xsl:element name="Order">
<xsl:apply-templates select=".//VORNR" />
</xsl:element>
</xsl:if>
</xsl:template>
<!-- Part Order List > Part Order > Operational BO Number -->
<xsl:template match="//VORNR">
<xsl:element name="./Order">
<xsl:apply-templates select="node()|#*"/>
<xsl:value-of select="text()"/>
</xsl:element>
</xsl:template>
<xsl:template name="HasOrIsValidPOLine">
<xsl:choose>
<xsl:when test="./OrderLineSource/text() != 'YTR'">VALID</xsl:when>
<xsl:otherwise>INVALID</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Can you provide me the solution or let me know where I am going wrong
First, your sample XML is not well-formed: It contains a closing </TIPOLPolicy> tag that doesn't match the starting <HKPolicy> tag. Change that to </HKPolicy> first.
After that, the following XSLT 1.0 does what you want:
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
<!-- Identity transform -->
<!-- Default priority 0 for root node and -0.5 for the rest -->
<xsl:template match="/ | node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<!-- Do nothing for Order elements whose OrderLine/OrderLineSource equals 'YTR' -->
<!-- Default priority 0.5 -->
<xsl:template match="Order[OrderLine/OrderLineSource = 'YTR']"/>
</xsl:stylesheet>
It makes use of the identity transform and different default priorities: The identity transform with a lower default priority copies the input to the output unless another template with a higher priority exists for a given input match. This is the case for Order elements whose OrderLine/OrderLineSource descendant contains the text value 'YTR'. Due to its higher default priority, the more specific template takes precedence over the identity transform. Since the template doesn't produce any output, any matching Order elements are removed from the output.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Order[OrderLine/OrderLineSource[string() = 'YTR']]"/>
</xsl:stylesheet>

XSLT - create new XML

I have one xml A and want to create a new xml B. The outer elements of B are different from A, but some of the child nodes are the same. I have written an XSL file but am struggling to get it right. Can anyone tell me where I am going wrong? How do I copy individual elements from A to B? The issue is, when the transformation happens, the text in A is copied to B. Looking at previous stack overflow questions, this is because there is an error in my xsl but I cant figure out what.
A:
<Request>
<Source>
<RequestorID Client="1" EMailAddress="test#test.com" Password="pwd"/>
<RequestorPreferences Country="JP" Currency="jpy" Language="en">
<RequestMode>SYNCHRONOUS</RequestMode>
</RequestorPreferences>
</Source>
<RequestDetails>
<SearchHotelPriceRequest>
<ItemDestination DestinationCode="LON" DestinationType="city"/>
<ItemCode>98i</ItemCode>
<PeriodOfStay>
<CheckInDate>2015-05-20</CheckInDate>
<CheckOutDate>2015-05-21</CheckOutDate>
</PeriodOfStay>
<IncludePriceBreakdown/>
<IncludeChargeConditions/>
<Rooms>
<Room Code="tb" NumberOfCots="0" NumberOfRooms="1">
<ExtraBeds/>
</Room>
</Rooms>
</SearchHotelPriceRequest>
</RequestDetails>
</Request>
B:
<WebRequest>
<RequestDetails>
<WebSearchHotelPriceRequest
CallCentreClientUI="2577"
Client="1"
Country="JP"
Currency="jpy"
Language="en"
LoginID="">
<GcPriceOptions ShowDeduped="true"/>
<ItemDestination DestinationCode="LON" DestinationType="city"/>
<ItemName></ItemName>
<ItemCode>98i</ItemCode>
<EffectiveDate>2014-08-15</EffectiveDate>
<StarRatingRange>
<Min>0</Min>
<Max>0</Max>
</StarRatingRange>
<PeriodOfStay>
<CheckInDate>2015-05-20</CheckInDate>
<CheckOutDate>2015-05-21</CheckOutDate>
</PeriodOfStay>
<IncludeChargeConditions/>
<Rooms>
<Room Code="tb" NumberOfRooms="1"></Room>
</Rooms>
<SiteId>008</SiteId>
</WebSearchHotelPriceRequest>
</RequestDetails>
</WebRequest>
XSL:
<?xml version="1.0"?>
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Request">
<xsl:element name="WebRequest">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="RequestDetails">
<xsl:element name="RequestDetails">
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="SearchHotelPriceRequest">
<xsl:element name="WebSearchHotelPriceRequest">
<xsl:attribute name="CallCentreClientUI">1</xsl:attribute>
<xsl:attribute name="Client">1</xsl:attribute>
<xsl:attribute name="Country">UK</xsl:attribute>
<xsl:attribute name="Currency">GBP</xsl:attribute>
<xsl:attribute name="Language">EN</xsl:attribute>
<xsl:attribute name="LoginID">100</xsl:attribute>
<SiteId>001</SiteId>
<EffectiveDate>01-01-99</EffectiveDate>
<xsl:apply-templates />
</xsl:element>
</xsl:template>
<xsl:template match="ItemDestination">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="ItemCode">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="ItemName">
<xsl:copy>
<xsl:copy-of select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="PeriodOfStay">
<xsl:copy>
<xsl:copy-of select="node()" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="IncludeChargeConditions">
<xsl:copy>
<xsl:copy-of select="node()" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="Rooms">
<xsl:copy>
<xsl:copy-of select="node()" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Incorrect Output:
<?xml version="1.0" encoding="UTF-8"?>
<WebRequest>SYNCHRONOUS<RequestDetails>
<WebSearchHotelPriceRequest CallCentreClientUI="1" Client="1" Country="UK" Currency="GBP" Language="EN" LoginID="100">
<SiteId>001</SiteId>
<EffectiveDate>01-01-99</EffectiveDate>
<ItemDestination DestinationCode="LON" DestinationType="city"/>
<ItemCode>98i</ItemCode>
<PeriodOfStay>
<CheckInDate>2015-05-20</CheckInDate>
<CheckOutDate>2015-05-21</CheckOutDate>2015-05-202015-05-21</PeriodOfStay>
<IncludeChargeConditions/>
<Rooms>
<Room Code="tb" NumberOfCots="0" NumberOfRooms="1">
<ExtraBeds/>
</Room>
</Rooms>
</WebSearchHotelPriceRequest>
</RequestDetails>
</WebRequest>
I think you are making this much more complicated than it needs to be. The simplest way to copy elements (along with all they contain, i.e. "deep copy") is to use xsl:copy-of. Also, you don't need to use xsl:element if you know the name of the element; just output the element literally. Similarly, for xsl:attribute you can write it directly and - if necessary - use attribute value template to insert the value from the input.
Have a look at the following stylesheet as an example:
<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="/">
<WebRequest>
<xsl:for-each select="Request/RequestDetails/SearchHotelPriceRequest">
<RequestDetails>
<WebSearchHotelPriceRequest
CallCentreClientUI="2577"
Client="2577"
Country="J"
Currency="USD"
Language="E"
LoginID="">
<GcPriceOptions ShowDeduped="true"/>
<xsl:copy-of select="ItemDestination"/>
<ItemName></ItemName>
<xsl:copy-of select="ItemCode"/>
<EffectiveDate>2014-08-15</EffectiveDate>
<StarRatingRange>
<Min>0</Min>
<Max>0</Max>
</StarRatingRange>
<xsl:copy-of select="PeriodOfStay | IncludeChargeConditions | Rooms"/>
<SiteId>008</SiteId>
</WebSearchHotelPriceRequest>
</RequestDetails>
</xsl:for-each>
</WebRequest>
</xsl:template>
</xsl:stylesheet>
This is of course not exactly what you need, but I don't know which values go where.
If someone could tell me why the extra text (e.g. SYNCHRONOUS) is
being erroneously copied?
Because you are using xsl:apply templates indiscriminately - and text nodes are copied by default using the built-in templates.

Extract Xpaths of all nodes and then their attributes

I am struggling with xslt from the past 2 days, owing to my starter status.My requirement is that given any input XML file ,I want the output to be a list of all the XPaths of all the tags in order in which they appear in the original XML document(parent, then parent,parents Attributes list/child, parent/child/childOFchild and so forth). THe XSLT should not be specific to any single XMl schema. It should work for any XML file, which is a valid one.
Ex:
If the Input XML Is :
<v1:Root>
<v1:UserID>test</v1:UserID>
<v1:Destination>test</v1:Destination>
<v1:entity name="entiTyName">
<v11:attribute name="entiTyName"/>
<v11:attribute name="entiTyName"/>
<v11:attribute name="entiTyName"/>
<v11:filter type="entiTyName">
<v11:condition attribute="entiTyName" operator="eq" value="{FB8D669E-D090-E011-8F43-0050568E222C}"/>
<v11:condition attribute="entiTyName" operator="eq" value="1"/>
</v11:filter>
<v11:filter type="or">
<v11:filter type="or">
<v11:filter type="and">
<v11:filter type="and">
<v11:condition attribute="cir_customerissuecode" operator="not-like" value="03%"/>
</v11:filter>
</v11:filter>
</v11:filter>
</v11:filter>
</v1:entity>
</v1:Root>
I want my output to be :
/v1:Root/v1:UserID
/v1:Root/v1:Destination
/v1:Root/v1:entity/#name
/v1:Root/v1:entity/v11:attribute
/v1:Root/v1:entity/v11:attribute/#name
/v1:Root/v1:entity/v11:attribute[2]
/v1:Root/v1:entity/v11:attribute[2]/#name
/v1:Root/v1:entity/v11:attribute[3]
/v1:Root/v1:entity/v11:attribute[3]/#name
/v1:Root/v1:entity/v11:filter/#type
/v1:Root/v1:entity/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter/v11:condition/#operator
/v1:Root/v1:entity/v11:filter/v11:condition/#value
/v1:Root/v1:entity/v11:filter/v11:condition[2]
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#attribute
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#operator
/v1:Root/v1:entity/v11:filter/v11:condition[2]/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/#type
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition/#value
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#attribute
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#operator
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]/#value
So, it is basically all the XPath of each element ,then the Xpath of the elements Attributes.
I have an XSLT with me, which is like this:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" />
<xsl:template match="*[not(child::*)]">
<xsl:for-each select="ancestor-or-self::*">
<xsl:value-of select="concat('/', name())" />
<xsl:if test="count(preceding-sibling::*[name() = name(current())]) != 0">
<xsl:value-of
select="concat('[', count(preceding-sibling::*[name() = name(current())]) + 1, ']')" />
</xsl:if>
</xsl:for-each>
<xsl:apply-templates select="*" />
</xsl:template>
<xsl:template match="/">
<xsl:apply-templates select="*" />
</xsl:template>
</xsl:stylesheet>
THe output which gets Produced does not cater to complex tags and also the tag's attributes in the resulting Xpath list :(.
Kindly help me in fixing this xslt to produce the output as mentioned above.
THe present output from the above XSLT is like this :
/v1:Root/v1:UserID
/v1:Root/v1:Destination
/v1:Root/v1:entity/v11:attribute
/v1:Root/v1:entity/v11:attribute[2]
/v1:Root/v1:entity/v11:attribute[3]
/v1:Root/v1:entity/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter/v11:filter/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition[2]
/v1:Root/v1:entity/v11:filter[2]/v11:filter[2]/v11:filter[2]/v11:condition[3]
I think there's a discrepancy between your sample input and output, in that the output describes a filter element with two conditions that's not in the source XML. At any rate, I believe this works:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" indent="no" />
<!-- Handle attributes -->
<xsl:template match="#*">
<xsl:apply-templates select="ancestor-or-self::*" mode="buildPath" />
<xsl:value-of select="concat('/#', name())"/>
<xsl:text>
</xsl:text>
</xsl:template>
<!-- Handle non-leaf elements (just pass processing downwards) -->
<xsl:template match="*[#* and *]">
<xsl:apply-templates select="#* | *" />
</xsl:template>
<!-- Handle leaf elements -->
<xsl:template match="*[not(*)]">
<xsl:apply-templates select="ancestor-or-self::*" mode="buildPath" />
<xsl:text>
</xsl:text>
<xsl:apply-templates select="#*" />
</xsl:template>
<!-- Outputs a path segment for the matched element: '/' + name() + [ordinalPredicate > 1] -->
<xsl:template match="*" mode="buildPath">
<xsl:value-of select="concat('/', name())" />
<xsl:variable name="sameNameSiblings" select="preceding-sibling::*[name() = name(current())]" />
<xsl:if test="$sameNameSiblings">
<xsl:value-of select="concat('[', count($sameNameSiblings) + 1, ']')" />
</xsl:if>
</xsl:template>
<!-- Ignore text -->
<xsl:template match="text()" />
</xsl:stylesheet>

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.