JSON And Cross Domain XML Requests

After finding a way to read RSS feeds in a custom help collection web page without cross domain restrictions, see previous blog post, I naturally wanted to do the same thing with XML feeds. You could probably figure that out for yourself given the RSS example but I hate it when I find sample code that does not quite do what I need. There were also some annoyances with my previous solution and I have some improvements.

The first step is to create a new generic handler to convert XML data to the JSON format:

<%@ WebHandler Language="C#" Class="xml2json" %>

using System;
using System.Web;
using System.Xml;
using System.Text;

using System.Collections;

public class xml2json : IHttpHandler {

    
public void ProcessRequest (HttpContext context) {
        context.Response.ContentType =
"text/javascript";
        
string strFeed;
        
string strJSON;
        strFeed =
HttpContext.Current.Request.QueryString["feed"];
        
XmlDocument xdoc = new XmlDocument();
        xdoc.Load(strFeed);
        
// Convert XML to a JSON string
        string JSON = XmlToJSON(xdoc);
        
// return an object
        strJSON = "var obj = eval (" + JSON + ");";
        context.Response.Write(strJSON);
    }

    
public bool IsReusable {
        
get {
            
return false;
        }
    }
}

As for the code to actually do the data format conversion, XmlToJSON, I just used the first sample code I found on the Interet which was How to convert XML to JSON in ASP.NET C# but there may be better methods available. The generic handler can be passed any XML feed web address through the query string value. I used the National Weather Service for my tests because I'm familiar with their XML format. I created a simple form for inputing the weather station ID:

<form id="frm" action="">
Enter Weather Station ID: <input type="text" id="station" size="20" />
<br /><br />	
<input type="button" value="Submit" id="btnSubmit" onclick="LoadJSON();" />
</form>

You also need a div where the response is going to appear. This div should appear above the JavaScript and the form should appear below the JavaScript:

<div id="response"></div>

In the JavaScript, I needed to make an improvement to deal with some latency in the response. I found I would need to click the submit button a few times before the JavaScript object would be available. So I added some code to test for the object and repeat the request if the object was undefined. This proved to be surprisingly difficult because it is tricky to test for an undefined JavaScript object without causing an error. That is something to add to my notes. I also needed to dynamically create a table so I learned a few more JavaScript DOM methods like insertRow and insertCell. I like it when these little research projects turn up some additional tips and tricks.

<script type="text/javascript">
// repeatedly calls AddScript until there is an object	
function LoadJSON() {
	if (document.getElementById("station").value != "") {
		AddScript();
		while (typeof(obj) == "undefined") {
			AddScript();
		}
		ShowWeather();
	}
	else {
		alert("Enter the Weather Station ID first!");
		document.getElementById("station").focus();
	}		
}
function AddScript() {
	jscript = document.createElement("script");
	jscript.setAttribute("type", "text/javascript");
	jscript.setAttribute("src", "http://localhost/rss2json/xml2json.ashx?feed=http://www.weather.gov/data/current_obs/" + document.getElementById("station").value + ".xml");
	document.getElementsByTagName('head')[0].appendChild(jscript);
}
function ShowWeather() {
    //for (prop in obj.current_observation) {
    //    alert(prop);
    //}
    // create table
    var table = document.createElement("table");
    table.setAttribute("bgcolor","#eeeeee");
    table.setAttribute("cellspacing","0");
    table.setAttribute("cellpadding","1");
    table.style.backgroundColor = "#eeeeee";

    // create 1st row
    var tr = table.insertRow(-1)
    tr.setAttribute("bgcolor","black");
    tr.style.backgroundColor = "black";
    tr.style.color = "white";
    var td = tr.insertCell(-1)
    td.setAttribute("colspan","2");
    td.colSpan = 2;
    var loc = document.createTextNode(obj.current_observation.location); 
    td.appendChild(loc);
    var br = document.createElement("br");
    td.appendChild(br);
    var time = document.createTextNode(obj.current_observation.observation_time); 
    td.appendChild(time);

    // create 2nd row
    var trWeather = table.insertRow(-1)
    var tdWeather = trWeather.insertCell(-1)
    var cellText = document.createTextNode("Weather:");
    tdWeather.appendChild(cellText);
    var tdWeatherValue = trWeather.insertCell(-1)
    var cellText = document.createTextNode(obj.current_observation.weather);
    tdWeatherValue.appendChild(cellText);

    // create 3rd row
    var trTemperature = table.insertRow(-1)
    var tdTemperature = trTemperature.insertCell(-1)
    var cellText = document.createTextNode("Temperature:");
    tdTemperature.appendChild(cellText);
    var tdTemperatureValue = trTemperature.insertCell(-1)
    var cellText = document.createTextNode(obj.current_observation.temperature_string);
    tdTemperatureValue.appendChild(cellText);

    // create 4th row
    var trHumidity = table.insertRow(-1)
    var tdHumidity = trHumidity.insertCell(-1)
    var cellText = document.createTextNode("Humidity:");
    tdHumidity.appendChild(cellText);
    var tdHumidityValue = trHumidity.insertCell(-1)
    var cellText = document.createTextNode(obj.current_observation.relative_humidity + " %");
    tdHumidityValue.appendChild(cellText);

    //create 5th row
    var trWind = table.insertRow(-1)
    var tdWind = trWind.insertCell(-1);
    var cellText = document.createTextNode("Wind:");
    tdWind.appendChild(cellText);
    var tdWindValue = trWind.insertCell(-1);
    var cellText = document.createTextNode(obj.current_observation.wind_string);
    tdWindValue.appendChild(cellText);

    //create 6th row based on a condition
    if (obj.current_observation.windchill_string != null) {
        var trWindChill = table.insertRow(-1)
        var tdWindChill = trWindChill.insertCell(-1);
        var cellText = document.createTextNode("WindChill:");
        tdWindChill.appendChild(cellText);
        var tdWindChillValue = trWindChill.insertCell(-1);
        var cellText = document.createTextNode(obj.current_observation.windchill_string);
        tdWindChillValue.appendChild(cellText);
    }
    else {
        var trHeatIndex = table.insertRow(-1)
        var tdHeatIndex = trHeatIndex.insertCell(-1);
        var cellText = document.createTextNode("HeatIndex:");
        tdHeatIndex.appendChild(cellText);
        var tdHeatIndexValue = trHeatIndex.insertCell(-1);
        var cellText = document.createTextNode(obj.current_observation.heat_index_string);
        tdHeatIndexValue.appendChild(cellText);
    }
    // create table body
    var tbody = document.createElement("tbody");
    table.appendChild(tbody);
    var div = document.getElementById("response");
    // add table to div
    div.appendChild(table);
}
</script>

In an initial effort to solve the latency problem, I did some research into precompiling the generic handler. I'd never tried to precompile an ASP.NET 2.0 web site before and getting the correct command line tool syntax required some experimentation. This is another instance of the documentation failing to provide enough examples! When I copied the staging files into the web site I had to delete the App_Code directory and lost the source code for my generic handler although I'd already added that to my notes:

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727>aspnet_compiler -v /rss2json -p 
"C:\Inetpub\wwwroot\rss2json" "C:\staging"
Utility to precompile an ASP.NET application
Copyright (C) Microsoft Corporation. All rights reserved.

This may actually be useful for me because I have several shipping rate web services that I need to document. Now I can build actual test scripts into my notes on these shipping rate services and eliminate the need to track down my test scripts. This project also taught me how to test for a JavaScript object, how to dynamically create a table, and how to precompile an ASP.NET 2.0 web application so it was a worthwhile exercise.

4 Comments

  • Hello!

    How do you call the properties from the object returned when you dinamically add the script? I understand the main concept, adding the script will put the JSON it returns in cache but how can access that JSON? Is is the "obj" you use? Is it a predefined name?

    Thanks in advance

  • Hello Pedro,

    Usually a JSON web service will provide a callback function but my generic handler just returns the object. The "obj" is a variable I created from evaluating the JSON string:

    strJSON = "var obj = eval (" + JSON + ");";

    All of the data the object contains can then be referenced as a property. I'll have to research how to implement a proper callback function which I believe is just a matter of figuring out the JavaScript syntax.

  • would be a good source for a long time I was looking for it ...

  • Wonderful post. I learned many interesting things. Thank you)

Comments have been disabled for this content.