Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old January 20th, 2011, 06:18 AM
Authorized User
 
Join Date: May 2009
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
Default simple transformation

I have an input xml

Code:
<?xml version="1.0" encoding="UTF-8"?>
<PackageReservation>
	<PassengerListItems>
		<PassengerListItem BirthDate="1986-01-12" Code="10" Gender="Male" RPH="1">
			<Name>
				<NamePrefix>Mr</NamePrefix>
				<GivenName>Mark</GivenName>
				<Surname>Dost</Surname>
			</Name>
		</PassengerListItem>
		<PassengerListItem BirthDate="1987-01-12" Code="10" Gender="Female" RPH="2">
			<Name>
				<NamePrefix>Mrs</NamePrefix>
				<GivenName>Claudia</GivenName>
				<Surname>Ollmann</Surname>
			</Name>
		</PassengerListItem>
		<PassengerListItem BirthDate="2000-01-12" Code="7" Gender="Female" RPH="3">
			<Name>
				<NamePrefix>Mr.</NamePrefix>
				<GivenName>Cla</GivenName>
				<Surname>B</Surname>
			</Name>
		</PassengerListItem>
	</PassengerListItems>
	<CostSummary>
		<CostingItems>
			<CostingItem CostBasis="7" PassengerRPH="1">
				<UnitCost Amount="50" CurrencyCode="EUR"/>
			</CostingItem>
			<CostingItem CostBasis="7" PassengerRPH="2">
				<UnitCost Amount="600" CurrencyCode="EUR"/>
			</CostingItem>
			<CostingItem CostBasis="7" PassengerRPH="3">
				<UnitCost Amount="20" CurrencyCode="EUR"/>
			</CostingItem>
		</CostingItems>
		<GrossAmount Amount="670" CurrencyCode="EUR"/>
	</CostSummary>
</PackageReservation>
Now in the above xml, attribute "Code=10" represents adult and attribute "RPH" inside PassengerListItem corresponds to "PassengerRPH" inside CostingItem.

What i want is to change the UnitCost Amount of the adult equals to the sum of all adults cost / total number of adults and the rest would remain as it is.

for example i want my out xml to look like this:

Code:
<?xml version="1.0" encoding="UTF-8"?>
<PackageReservation>
	<PassengerListItems>
		<PassengerListItem BirthDate="1986-01-12" Code="10" Gender="Male" RPH="1">
			<Name>
				<NamePrefix>Mr</NamePrefix>
				<GivenName>Mark</GivenName>
				<Surname>Dost</Surname>
			</Name>
		</PassengerListItem>
		<PassengerListItem BirthDate="1987-01-12" Code="10" Gender="Female" RPH="2">
			<Name>
				<NamePrefix>Mrs</NamePrefix>
				<GivenName>Claudia</GivenName>
				<Surname>Ollmann</Surname>
			</Name>
		</PassengerListItem>
		<PassengerListItem BirthDate="2000-01-12" Code="7" Gender="Female" RPH="3">
			<Name>
				<NamePrefix>Mr.</NamePrefix>
				<GivenName>Cla</GivenName>
				<Surname>B</Surname>
			</Name>
		</PassengerListItem>
	</PassengerListItems>
	<CostSummary>
		<CostingItems>
			<CostingItem CostBasis="7" PassengerRPH="1">
				<UnitCost Amount="325" CurrencyCode="EUR"/>
			</CostingItem>
			<CostingItem CostBasis="7" PassengerRPH="2">
				<UnitCost Amount="325" CurrencyCode="EUR"/>
			</CostingItem>
			<CostingItem CostBasis="7" PassengerRPH="3">
				<UnitCost Amount="20" CurrencyCode="EUR"/>
			</CostingItem>
		</CostingItems>
		<GrossAmount Amount="670" CurrencyCode="EUR"/>
	</CostSummary>
</PackageReservation>

In my partially created xslt 1 script, i have a variable which contains the total of all adult costs i.e. 650.

Code:
<xsl:comment>
	<xsl:value-of select="$adult_persons_prices"/>
</xsl:comment>

What i need is the template for creating CostSummary with modified Amount for adults as shown above.

Please someone could help?
 
Old January 20th, 2011, 06:40 AM
samjudson's Avatar
Friend of Wrox
 
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
Default

First it is easiest to start with a identity template like so:

Code:
<xsl:template match="@*|node()">
	<xsl:copy>
		<xsl:apply-templates select="@*|node()"/>
	</xsl:copy>
</xsl:template>
Then seeing as we are going to be looking up these adults quite a few times add a key for looking up passengers:

Code:
<xsl:key name="passengers" match="PassengerListItem" use="@RPH"/>
The adults prices and count are not going to change so these can be global variables which use the above key, like so:

Code:
<xsl:variable name="sumAdults" select="sum(//CostingItem[key('passengers',@PassengerRPH)/@Code='10']/UnitCost/@Amount)"/>
<xsl:variable name="countAdults" select="count(//CostingItem[key('passengers',@PassengerRPH)/@Code='10'])"/>
I don't whether you can have more than one CostingItem per passenger - if you can't then the above countAdult could be made a little simpler.

Finally write a template to handle CostingItems, and check if the passenger is an adult:

Code:
<xsl:template match="CostingItem">
	<xsl:choose>
		<xsl:when test="key('passengers',@PassengerRPH)/@Code='10'">
			<xsl:copy>
				<xsl:apply-templates select="@*"/>
				<UnitCost Amount="{$sumAdults div $countAdults}" CurrencyCode="{./UnitCost/@CurrencyCode}"/>
				</xsl:copy>
			</xsl:when>
			<xsl:otherwise><xsl:copy-of select="."/></xsl:otherwise>
		</xsl:choose>
</xsl:template>
Simples!
__________________
/- Sam Judson : Wrox Technical Editor -/

Think before you post: What have you tried?
 
Old January 20th, 2011, 06:45 AM
mhkay's Avatar
Wrox Author
 
Join Date: Apr 2004
Posts: 4,962
Thanks: 0
Thanked 292 Times in 287 Posts
Default

First you need an identity template that copies everything unchanged unless otherwise specified:

Code:
<xsl:template match="node() | @*">
  <xsl:copy>
     <xsl:apply-templates select="node() | @*"/>
  </xsl:copy>
</xsl:template>
and then you need to "otherwise specify":

Code:
  
<xsl:variable name="adults" select="//PassengerListItem[@Code='10']/@RBH"/>
 <xsl:variable name="adultCostings" select="//CostingItems[@PassengerRPH=$adults]"/>
 <xsl:variable name="averageAdultCosting" select="sum($adultCostings/UnitCost/@Amount) div count($adults)
<xsl:template match="CostingItems[@PassengerRPH=$adults]/UnitCost/@Amount">
  <xsl:attribute name="UnitCost" select="$averageAdultCosting"/>
</xsl:template>
Not tested

<xsl:variable name="average"
__________________
Michael Kay
http://www.saxonica.com/
Author, XSLT 2.0 and XPath 2.0 Programmer\'s Reference
 
Old January 20th, 2011, 06:48 AM
Friend of Wrox
 
Join Date: Nov 2007
Posts: 1,243
Thanks: 0
Thanked 245 Times in 244 Posts
Default

With XSLT 2.0 you could do it like this:
Code:
<xsl:stylesheet
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  version="2.0">
  
  <xsl:param name="code" select="10"/>
  
  <xsl:key name="k1" match="CostingItem/UnitCost" use="../@PassengerRPH"/>
  
  <xsl:output indent="yes"/>
  <xsl:strip-space elements="*"/>
  
  <xsl:variable name="pc" select="//PassengerListItem[@Code = $code]/@RPH"/>
  <xsl:variable name="code-sum" select="sum(key('k1', $pc)/@Amount)"/>
  <xsl:variable name="n" select="count($pc)"/>
  <xsl:variable name="amount" select="$code-sum div $n"/>
  
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
  
  <xsl:template match="key('k1', $pc)/@Amount">
    <xsl:attribute name="Amount"><xsl:value-of select="$amount"/></xsl:attribute>
  </xsl:template>

</xsl:stylesheet>
__________________
Martin Honnen
Microsoft MVP (XML, Data Platform Development) 2005/04 - 2013/03
My blog
 
Old January 20th, 2011, 06:49 AM
samjudson's Avatar
Friend of Wrox
 
Join Date: Aug 2007
Posts: 2,128
Thanks: 1
Thanked 189 Times in 188 Posts
Default

There you go, three slightly different ways of doing the same thing - isn't XSLT wonderful :)
__________________
/- Sam Judson : Wrox Technical Editor -/

Think before you post: What have you tried?
 
Old January 20th, 2011, 07:03 AM
Authorized User
 
Join Date: May 2009
Posts: 32
Thanks: 0
Thanked 0 Times in 0 Posts
Default

thank you VERY MUCH guys!

I did it on mym own:) , here is my solution.

Code:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.opentravel.org/OTA/2003/05" xmlns:ota="http://www.opentravel.org/OTA/2003/05" exclude-result-prefixes="ota xsl">
	<xsl:output encoding="UTF-8" version="1.0" method="xml"/>
	<xsl:strip-space elements="*"/>
	<xsl:variable name="no_of_adult" select="count(//ota:PassengerListItems/ota:PassengerListItem[@Code = '10'])"/>
	<xsl:variable name="no_of_children" select="count(//ota:PassengerListItems/ota:PassengerListItem[@Code != '10'])"/>
	<xsl:variable name="adult_persons_prices">
		<xsl:call-template name="adult">
			<xsl:with-param name="nodelist_adult" select="//ota:PassengerListItems/ota:PassengerListItem[@Code = '10']"/>
			<xsl:with-param name="nodelist" select="//ota:CostingItem"/>
		</xsl:call-template>
	</xsl:variable>
	<xsl:template name="adult">
		<xsl:param name="nodelist_adult"/>
		<xsl:param name="nodelist"/>
		<xsl:param name="counter">1</xsl:param>
		<xsl:param name="sum">0.00</xsl:param>
		<xsl:variable name="rph">
			<xsl:value-of select="$nodelist_adult[$counter]/@RPH"/>
		</xsl:variable>
		<xsl:variable name="sum_temp">
			<xsl:choose>
				<xsl:when test="$nodelist[@PassengerRPH = $rph]/ota:UnitCost/@Amount">
					<xsl:value-of select="$sum + $nodelist[@PassengerRPH = $rph]/ota:UnitCost/@Amount"/>
				</xsl:when>
				<xsl:when test="$nodelist[@PassengerRPH = $rph]/ota:Cost/@Amount">
					<xsl:value-of select="$sum + $nodelist[@PassengerRPH = $rph]/ota:ExtendedCost/@Amount"/>
				</xsl:when>
			</xsl:choose>
		</xsl:variable>
		<xsl:choose>
			<xsl:when test="$counter &lt; count($nodelist_adult)">
				<xsl:call-template name="adult">
					<xsl:with-param name="nodelist_adult" select="$nodelist_adult"/>
					<xsl:with-param name="nodelist" select="$nodelist"/>
					<xsl:with-param name="counter" select="$counter + 1"/>
					<xsl:with-param name="sum" select="$sum_temp"/>
				</xsl:call-template>
			</xsl:when>
			<xsl:otherwise>
				<xsl:value-of select="$sum_temp"/>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
	
	
	
	<xsl:template match="/">
		<xsl:comment>
			<xsl:value-of select="$adult_persons_prices"/>
		</xsl:comment>
		<xsl:apply-templates select="node()"/>
	</xsl:template>
	<xsl:template match="*">
		<xsl:element name="{local-name()}">
			<xsl:apply-templates select="@*"/>
			<xsl:apply-templates select="*"/>
			<xsl:copy-of select="text()"/>
		</xsl:element>
	</xsl:template>
	<xsl:template match="@*">
		<xsl:copy-of select="."/>
	</xsl:template>
	<xsl:template match="ota:CostSummary">
		<xsl:element name="{local-name()}">
			<xsl:copy-of select="@*"/>
			<CostingItems>
				<xsl:apply-templates select="//ota:PassengerListItem[@Code='10']" mode="pricePerPerson_adult"/>
				<xsl:apply-templates select="//ota:PassengerListItem[@Code!='10']" mode="pricePerPerson_children"/>
			</CostingItems>
			<xsl:apply-templates select="ota:GrossAmount" mode="copy"/>
		</xsl:element>
	</xsl:template>
	<xsl:template match="ota:PassengerListItem" mode="pricePerPerson_adult">
		<xsl:variable name="passengers_count_adult" select="count(//ota:PassengerListItems/ota:PassengerListItem[@Code = '10'])"/>
		<xsl:variable name="person_price_adult" select="$adult_persons_prices div $passengers_count_adult"/>
		<CostingItem CostBasis="7" Description="Per Person">
			<xsl:attribute name="PassengerRPH">
				<xsl:value-of select="@RPH"/>
			</xsl:attribute>
			<UnitCost>
				<xsl:if test="//ota:GrossAmount/@CurrencyCode">
					<xsl:copy-of select="//ota:GrossAmount/@CurrencyCode"/>
				</xsl:if>
				<xsl:attribute name="Amount">
					<xsl:value-of select="format-number($person_price_adult, '#0.00')"/>
				</xsl:attribute>
			</UnitCost>
		</CostingItem>
	</xsl:template>
	<xsl:template match="ota:PassengerListItem" mode="pricePerPerson_children">
		<xsl:variable name="rph" select="@RPH"/>
		<CostingItem CostBasis="7" Description="Per Person">
			<xsl:attribute name="PassengerRPH">
				<xsl:value-of select="@RPH"/>
			</xsl:attribute>
			<UnitCost>
				<xsl:if test="//ota:GrossAmount/@CurrencyCode">
					<xsl:copy-of select="//ota:GrossAmount/@CurrencyCode"/>
				</xsl:if>
				<xsl:attribute name="Amount">
					<xsl:value-of select="format-number(//ota:CostingItem[@PassengerRPH = $rph]/ota:UnitCost/@Amount, '#0.00')"/>
				</xsl:attribute>
			</UnitCost>
		</CostingItem>
	</xsl:template>
	<xsl:template match="ota:GrossAmount" mode="copy">
		<GrossAmount>
			<xsl:copy-of select="@*"/>
		</GrossAmount>
	</xsl:template>
</xsl:stylesheet>

leave the first template, as it is for getting the sum.

Thanks again.





Similar Threads
Thread Thread Starter Forum Replies Last Post
Result of Transformation PKHG BOOK: XSLT 2.0 and XPath 2.0 Programmer's Reference, 4th Edition ISBN: 978-0-470-19274-0 4 August 6th, 2008 10:57 AM
using transformation KieshaJ310 ASP.NET 2.0 Basics 2 June 6th, 2007 05:56 AM
polynomial transformation star79_sofi Pro VB 6 1 December 6th, 2004 03:06 PM
XSL transformation Thodoris XML 0 May 20th, 2004 08:33 AM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.