[Okta Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Calculations in XSL

Posted on 2011-10-16
7
Medium Priority
?
204 Views
Last Modified: 2012-05-12
I'm calling the following XML feed;

http://www.google.co.uk/ig/api?weather=sydney

<?xml version="1.0"?>
<xml_api_reply version="1">
  <weather module_id="0" tab_id="0" mobile_row="0" mobile_zipped="1" row="0" section="0">
    <forecast_information>
      <city data="Sydney, NSW"/>
      <postal_code data="sydney"/>
      <latitude_e6 data=""/>
      <longitude_e6 data=""/>
      <forecast_date data="2011-10-17"/>
      <current_date_time data="2011-10-17 17:07:00 +0000"/>
      <unit_system data="US"/>
    </forecast_information>
    <current_conditions>
      <condition data="Partly Cloudy"/>
      <temp_f data="63"/>
      <temp_c data="17"/>
      <humidity data="Humidity: 45%"/>
      <icon data="/ig/images/weather/partly_cloudy.gif"/>
      <wind_condition data="Wind: S at 22 mph"/>
    </current_conditions>
    <forecast_conditions>
      <day_of_week data="Mon"/>
      <low data="54"/>
      <high data="64"/>
      <icon data="/ig/images/weather/sunny.gif"/>
      <condition data="Clear"/>
    </forecast_conditions>
    <forecast_conditions>
      <day_of_week data="Tue"/>
      <low data="52"/>
      <high data="64"/>
      <icon data="/ig/images/weather/mostly_sunny.gif"/>
      <condition data="Mostly Sunny"/>
    </forecast_conditions>
    <forecast_conditions>
      <day_of_week data="Wed"/>
      <low data="54"/>
      <high data="72"/>
      <icon data="/ig/images/weather/sunny.gif"/>
      <condition data="Clear"/>
    </forecast_conditions>
    <forecast_conditions>
      <day_of_week data="Thu"/>
      <low data="59"/>
      <high data="77"/>
      <icon data="/ig/images/weather/sunny.gif"/>
      <condition data="Clear"/>
    </forecast_conditions>
  </weather>
</xml_api_reply>

Open in new window



Using this XSL
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*/*">
        <div>
            <xsl:attribute name="id">
                <xsl:apply-templates select="." mode="id"/>
            </xsl:attribute>
            <xsl:apply-templates select="node()|@*"/>
        </div>
    </xsl:template>
    <xsl:template match="postal_code|latitude_e6|longitude_e6|temp_f"/>
    <xsl:template match="*" mode="id">
        <xsl:value-of select="concat(name(),'_data')"/>
    </xsl:template>
    <xsl:template match="forecast_information|current_conditions|
                         forecast_conditions|icon|forecast_date" mode="id">
        <xsl:value-of select="name()"/>
    </xsl:template>
</xsl:stylesheet>

Open in new window



How do I change the XSL to ouput the low_data and high_data values in Celsius rather than Fahrenheit ?

The formula for Fahrenheit to Celsius is (°F  -  32)  x  5/9 = °C

The article here at http://www.xml.com/pub/a/2001/05/07/xsltmath.html suggests I can also round the result (would like the value rounded to 1 decimal place)

<div id="weather_data">000100
  <div id="forecast_information">
    <div id="city_data">Sydney, NSW</div>
    <div id="postal_code_data">sydney</div>
    <div id="latitude_e6_data"></div>
    <div id="longitude_e6_data"></div>
    <div id="forecast_date">2011-10-17</div>
    <div id="current_date_time_data">2011-10-17 17:30:00 +0000</div>
    <div id="unit_system_data">US</div>
  </div>
  <div id="current_conditions">
    <div id="condition_data">Partly Cloudy</div>
    <div id="temp_f_data">63</div>
    <div id="temp_c_data">17</div>
    <div id="humidity_data">Humidity: 48%</div>
    <div id="icon">/ig/images/weather/partly_cloudy.gif</div>
    <div id="wind_condition_data">Wind: S at 21 mph</div>
  </div>
  <div id="forecast_conditions">
    <div id="day_of_week_data">Mon</div>
    <div id="low_data">54</div>
    <div id="high_data">64</div>
    <div id="icon">/ig/images/weather/sunny.gif</div>
    <div id="condition_data">Clear</div>
  </div>
  <div id="forecast_conditions">
    <div id="day_of_week_data">Tue</div>
    <div id="low_data">52</div>
    <div id="high_data">64</div>
    <div id="icon">/ig/images/weather/mostly_sunny.gif</div>
    <div id="condition_data">Mostly Sunny</div>
  </div>
  <div id="forecast_conditions">
    <div id="day_of_week_data">Wed</div>
    <div id="low_data">54</div>
    <div id="high_data">72</div>
    <div id="icon">/ig/images/weather/sunny.gif</div>
    <div id="condition_data">Clear</div>
  </div>
  <div id="forecast_conditions">
    <div id="day_of_week_data">Thu</div>
    <div id="low_data">59</div>
    <div id="high_data">77</div>
    <div id="icon">/ig/images/weather/sunny.gif</div>
    <div id="condition_data">Clear</div>
  </div>
</div>

Open in new window

0
Comment
Question by:sonic1234
  • 3
  • 2
  • 2
7 Comments
 
LVL 3

Assisted Solution

by:varontron
varontron earned 200 total points
ID: 36977558
Assuming your context node is the outer <div>,

Your low_data C value =

<xsl:value-of select="round((number(./div[@id='low_data']) - 32) * (5 div 9) )/>

Open in new window


You can get fancy by checking to ensure low_data and high_data are actually numbers, i.e.,

<xsl:if test="not(string(number(./div[@id='low_data']))) = 'NaN'>
<xsl:value-of select="round((number(./div[@id='low_data']) - 32) * (5 div 9) )/>
</xsl:if>

Open in new window


Hope this helps,
Dave
0
 

Author Comment

by:sonic1234
ID: 36977603
Thank you for your help so far.  Excuse my ignorance, I assume I would insert this code somewhere near line 17, and would need to add some sort of filter conditional so that the calculation is only run on the low_data and high_data nodes?

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="*/*">
        <div>
            <xsl:attribute name="id">
                <xsl:apply-templates select="." mode="id"/>
            </xsl:attribute>
            <xsl:apply-templates select="node()|@*"/>
        </div>
    </xsl:template>
    <xsl:template match="postal_code|latitude_e6|longitude_e6|temp_f"/>
    <xsl:template match="*" mode="id">
        <xsl:value-of select="concat(name(),'_data')"/>
    </xsl:template>
    <xsl:template match="forecast_information|current_conditions|
                         forecast_conditions|icon|forecast_date" mode="id">
        <xsl:value-of select="name()"/>
        <xsl:if test="not(string(number(./div[@id='low_data']))) = 'NaN'>
        <xsl:value-of select="round((number(./div[@id='low_data']) - 32) * (5 div 9) )/>
        </xsl:if>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36977971
Some suggestions here

match="*/*" is a very poor rule
it forces an extra check on every element, just drop it
you don't want to do something special on every element that has a parent.
You want to do something special on the root node (the only exception)
so instead of metch="*/*" do match="*"
and add the exception rule for match="/*"

if you do that, this will actually do something, since it is now more specific than other matches
    <xsl:template match="postal_code|latitude_e6|longitude_e6|temp_f"/>
in your current stylesheet it is doing nothing
I assume you want to get rid of those nodes, use a stropsace to make the rult look nicer

once you have done that you can add a special rule for low and high
    <xsl:template match="low | high">
with the logic in it

Now some minor flaws in the already suggested solution

- it works on teh result not on the source
- The result would be empty if this is not a number, I suggest to use a choose instead of an if
- It doesn't round as you wanted, it just rounds to integer, use format-number instead (it also lacks a quote by the way)
               <xsl:value-of select="format-number((. - 32) * (5 div 9) , '##.0')"/>

I would wrap all of that in a named template (some sort of a function)

I have attached the full stylesheet as I would suggest

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="/*">
        <xsl:apply-templates select="node()"/>
    </xsl:template>

    <xsl:template match="*">
        <div>
            <xsl:attribute name="id">
                <xsl:apply-templates select="." mode="id"/>
            </xsl:attribute>
            <xsl:apply-templates select="node()|@*"/>
        </div>
    </xsl:template>
    
    <xsl:template match="postal_code|latitude_e6|longitude_e6|temp_f"/>

    <xsl:template match="low | high">
        <div>
            <xsl:attribute name="id">
                <xsl:value-of select="concat(name(), '_data')"/>
            </xsl:attribute>
            <xsl:call-template name="farenheit-to-celsius">
                <xsl:with-param name="temp" select="@data"/>
            </xsl:call-template>
        </div>
    </xsl:template>
    
    <xsl:template match="*" mode="id">
        <xsl:value-of select="concat(name(),'_data')"/>
    </xsl:template>
    
    <xsl:template match="forecast_information|current_conditions|
        forecast_conditions|icon|forecast_date" mode="id">
        <xsl:value-of select="name()"/>
    </xsl:template>
    
    <xsl:template name="farenheit-to-celsius">
        <xsl:param name="temp"/>
        <xsl:choose>
            <xsl:when test="not(string(number($temp)) = 'NAN')">
                <xsl:value-of select="format-number(($temp - 32) * (5 div 9) , '##.0')"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$temp"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
LVL 60

Accepted Solution

by:
Geert Bormans earned 1800 total points
ID: 36977989
I have slightly reorganised your code, as I would write it,
rely more on the mechanism of preference and drop the mode
I also added an explict match for the data attribute
You will thank me once you have to do maintenance on this code and lost the approach from memory
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output indent="yes"/>
    <xsl:strip-space elements="*"/>
    
    <xsl:template match="/*">
        <xsl:apply-templates select="node()"/>
    </xsl:template>

    <xsl:template match="*">
        <div>
            <xsl:attribute name="id">
                <xsl:value-of select="concat(name(),'_data')"/>
            </xsl:attribute>
            <xsl:apply-templates select="node()|@data"/>
        </div>
    </xsl:template>
    
    <xsl:template match="forecast_information|current_conditions|forecast_conditions|icon|forecast_date">
        <div>
            <xsl:attribute name="id">
                <xsl:value-of select="name()"/>
            </xsl:attribute>
            <xsl:apply-templates select="node()|@data"/>
        </div>
    </xsl:template>
    
    <xsl:template match="postal_code|latitude_e6|longitude_e6|temp_f"/>

    <xsl:template match="low | high">
        <div>
            <xsl:attribute name="id">
                <xsl:value-of select="concat(name(), '_data')"/>
            </xsl:attribute>
            <xsl:call-template name="farenheit-to-celsius">
                <xsl:with-param name="temp" select="@data"/>
            </xsl:call-template>
        </div>
    </xsl:template>
    
    <xsl:template match="*" mode="id">
        <xsl:value-of select="concat(name(),'_data')"/>
    </xsl:template>
    
    <xsl:template name="farenheit-to-celsius">
        <xsl:param name="temp"/>
        <xsl:choose>
            <xsl:when test="not(string(number($temp)) = 'NAN')">
                <xsl:value-of select="format-number(($temp - 32) * (5 div 9) , '##.0')"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="$temp"/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

Open in new window

0
 

Author Closing Comment

by:sonic1234
ID: 36978655
Outstanding help
0
 
LVL 60

Expert Comment

by:Geert Bormans
ID: 36978657
welcome
0
 
LVL 3

Expert Comment

by:varontron
ID: 36978829
Oops.  good point on the if test.  Thx, @Gertone.
0

Featured Post

New feature and membership benefit!

New feature! Upgrade and increase expert visibility of your issues with Priority Questions.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

The Confluence of Individual Knowledge and the Collective Intelligence At this writing (summer 2013) the term API (http://dictionary.reference.com/browse/API?s=t) has made its way into the popular lexicon of the English language.  A few years ago, …
I was working on a PowerPoint add-in the other day and a client asked me "can you implement a feature which processes a chart when it's pasted into a slide from another deck?". It got me wondering how to hook into built-in ribbon events in Office.
In a question here at Experts Exchange (https://www.experts-exchange.com/questions/29062564/Adobe-acrobat-reader-DC.html), a member asked how to create a signature in Adobe Acrobat Reader DC (the free Reader product, not the paid, full Acrobat produ…
Despite its rising prevalence in the business world, "the cloud" is still misunderstood. Some companies still believe common misconceptions about lack of security in cloud solutions and many misuses of cloud storage options still occur every day. …

873 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question