Home > Software design >  How to access XML data with Coldfusion
How to access XML data with Coldfusion

Time:10-21

A client wants me to add a weather forecast to his web site. The official weather report comes in an XML file and I need help accessing some of the elements in the file.

I can download the two XML files that contain the data needed on the site, and I can parse them into ColdFusion XML variables.

I can extract the data I need from the top levels, but it's lower levels that are causing me some heartburn. The XML files contain weather observations and forecasts for every location in the state. We don't need that - we just want to access the data about my client's location.

Here's a sample of the XML data I'm talking about

<area aac="NSW_PT123" description="Richmond" type="location" parent-aac="NSW_PW005">
        <forecast-period index="0" start-time-local="2021-10-16T17:00:00 11:00" end-time-local="2021-10-17T00:00:00 11:00" start-time-utc="2021-10-16T06:00:00Z" end-time-utc="2021-10-16T13:00:00Z">
            <element type="forecast_icon_code">2</element>
            <text type="precis">Clear.</text>
            <text type="probability_of_precipitation">5%</text>
        </forecast-period>
        <forecast-period index="1" start-time-local="2021-10-17T00:00:00 11:00" end-time-local="2021-10-18T00:00:00 11:00" start-time-utc="2021-10-16T13:00:00Z" end-time-utc="2021-10-17T13:00:00Z">
            <element type="forecast_icon_code">1</element>
            <element type="air_temperature_minimum" units="Celsius">7</element>
            <element type="air_temperature_maximum" units="Celsius">24</element>
            <text type="precis">Sunny.</text>
            <text type="probability_of_precipitation">0%</text>
        </forecast-period>
        <forecast-period index="2" start-time-local="2021-10-18T00:00:00 11:00" end-time-local="2021-10-19T00:00:00 11:00" start-time-utc="2021-10-17T13:00:00Z" end-time-utc="2021-10-18T13:00:00Z">
            <element type="forecast_icon_code">1</element>
            <element type="air_temperature_minimum" units="Celsius">7</element>
            <element type="air_temperature_maximum" units="Celsius">28</element>
            <text type="precis">Sunny.</text>
            <text type="probability_of_precipitation">5%</text>
        </forecast-period>

In this snippet, index="0" indicates 'today' and index="1" indicates 'tomorrow'. The client wants outlook for 7 days.

So currently out of all that XML data I need to identify which day it's about, the icon code (for the pretty pictures) and the Precis (usually a single word or short phrase), probability_of_precipitation (is it gonna rain?) and the times. The rest I can throw away.

In another file with similar structure, there's a long form forecast for another version of the page.

I've been able to access the attributes in the first line (aac and description) but what I need help with is how to access the forecast-period elements under that line. There are forecast-period elements for all the other districts too, which we don't want to access.

I get to this part of the XML file with the ColdFusion XmlSearch/XPATH expression

XmlSearch(IDN10064XML, "//area[@aac='NSW_PT123']")

So here's my question. In the example code above, (which i have no control over - it's what the government puts out) how do I create a variable that will give me the results

  • "Clear." from the above precis element?
  • "5%" from the probability of precipitation element?
  • "2" from the forecast_icon_code element?

CodePudding user response:

<cffunction name="WeatherForecast" returntype="struct">
  <cfargument name="weatherData" type="xml" required="yes">
  <cfargument name="areaCode" type="string" required="yes">

  <cfset var areas = XmlSearch(weatherData, "//area[@aac='#areaCode#']")>
  <cfif ArrayLen(areas) eq 0>
    <cfthrow message="WeatherForecast: area code #areaCode# not found.">
  </cfif>
  
  <cfset var textOnly = function (node) {
    return node.XmlType == 'ATTRIBUTE' ? node.XmlValue : node.XmlText;
  }>

  <cfreturn {
    icons: XmlSearch(areas[1], "./forecast-period/element[@type='forecast_icon_code']").map(textOnly),
    precis: XmlSearch(areas[1], "./forecast-period/text[@type='precis']").map(textOnly),
    precipitation: XmlSearch(areas[1], "./forecast-period/text[@type='probability_of_precipitation']").map(textOnly)
  }>
</cffunction>

Usage:

<cfset IDN10064XML = XmlParse(weatherXml, true)>
<cfset forecast = WeatherForecast(IDN10064XML, "NSW_PT123")>

returns this struct:

{
    "PRECIPITATION": ["5%", "0%", "5%"],
    "PRECIS": ["Clear.", "Sunny.", "Sunny."],
    "ICONS": ["2", "1", "1"]
}

Each part will contain as many values as there are <forecast-period> elements for that <area> in the XML, in the order they appear in the XML. In other words, forecast.precipitation[1] will refer to "today", unless there is a chance that the XML ever arrives out of order (I doubt it).

Things like the time attribute values can be extracted in the same way:

XmlSearch(areas[1], "./forecast-period/@start-time-local").map(textOnly)

CodePudding user response:

I prefer converting simple XML to JSON and then using regular struct/array handling.

You can convert XML to JSON in Adobe ColdFusion using 1 line of code.

<CFSET JSONText = createObject("java","org.json.XML").toJSONObject(XMLText)>

For more examples, check out the sample CFML here: https://gist.github.com/JamoCA/00bb362672f772fab56d26f3e01ad3fa

  • Related