Sie sind auf Seite 1von 15

client script >> 

JSON in ASP.NET Ajax: First words 
Posted on Monday, October  22, 2007 2:49 AM

If you are a web developer, and you haven't been living under a rock for most of the past year, you must at least have  

heard of JSON. In the wake of the tremendous hype relating to everything even vaguely AJAX­related, JSON has 

climbed in record time the list of the top 10 technologies a web developer has to master.

Let's make it clear from the start: JSON does deserve a top spot in such a list, if nothing else because it's the skeleton 

that keeps the whole AJAX paradigm on its feet. Even more so for us .NET programmers. In fact, we'd be more 

accurate if we called it AJAJ, although I agree that would feel quite stupid   . As a matter of fact, when using 

ASP.NET AJAX, most of the communication between client and server is serialized in JSON format rather than XML, 

at least when using the default settings. And there's good reason for that. 

In fact, JSON is a very simple human­readable, easily­parsable data­interchange format, like XML, but unlike the 

latter, it is very concise, and, more importantly, it is immediately convertible to a Javascript object. That is, if you 

simply eval() a JSON expression, you get returned the actual object that was serialized in the first place. 

For those of you who might not know it, eval() is a built­in javascript function that evaluates a string and executes it as 

a line of code (see here for more info).

For example, you might serialize a very simple "Person" object that has properties "firstName", "lastName", and "age" 

as follows: 

{"firstName":"Marco","lastName":"Anastasi","age":27}

Note that property names are always enclosed in double quotes, while values follow the usual javascript syntax.

In Javascript, you would then be able to use code like this:

var json = '{"firstName":"Marco", "lastName":"Anastasi", "age":27}';

var person = eval('(' + json + ')');

alert(person.firstName);

Note that we enclose the json expression we are evaluating in parentheses, to instruct eval() to treat the string as an 

expression rather than a block of code.
WARNING: what you see here is merely used as an example. You should avoid resorting to the eval() function in a  

production environment, because of potential security breaches. There are valid alternatives out there, and a good  

place to start looking for them is, of course www.json.org. 

In my next posts I'll talk profusely about one of the possible solutions to this problem (and probably the one that 

ASP.NET developers are keener to use), i.e. ASP.NET AJAX's built­in client side JSON serializer/deserializer.

What you've seen so far is obviously a very simplified introduction to JSON, and doesn't show much of what you 

might achieve by leveraging its power and simplicity. In my next post, I'll delve deeper into its inner workings and how 

to use it to manage the communication between client side script and a server­side application, and finally to retrieve 

information from web services, shape it into different formats and serve it to the client browser.

<< JSON in ASP.NET Ajax: First words | Home | Data Interpolation with SPLINE in C# >> 

JSON in ASP.NET Ajax: Part 2. Calling a remote JSON Web 
Service from client script 
Posted on Wednes day, October  24, 2007 2:00 AM

Now, after the very futile example which accompanied the introduction to JavaScript Object Notation in my last post 

(by the way, I forgot to mention that this is what the fancy acronym stands for, but I'm sure you already knew that), 

let's go straight to the fun part, and see how we can leverage the flexibility of JSON (and ASP.NET AJAX, of course) 

to achieve some less trivial result. When developing a web application, we often need to aggregate data from different 

sources. You might argue this is no big news, as we've all used web services in a way or the other, and "mashup" is 

surely not an uncommon word these days. But probably, for most of us, the natural way to think of data returned from 

a web service call is XML. But XML is not exactly the easiest thing to parse on the client, nor a very succinct format. 

That's why a number of JSON web services have flourished lately. Probably the first that comes to mind is Google 

Maps'  Geocoder (see  here),   but there  are a  lot more. If  you are  looking for inspiration you  can have a  look at 

xmethods. But today, we are focusing on some very, very interesting web methods kindly offered by Geonames. 

In fact, as I've always mantained that an example is worth a googolplex words, in this post I'm going to describe a 

specific problem I had to solve recently, and the rather simple solution I found, using JSON and ASP.NET AJAX.  

So, let's get this started: enter

The Problem.
Let's imagine we want to retrieve the most recent weather information for a specific location, given its geographic 

coordinates. In particular, let's imagine that we let the user input the coordinates directly on the client (for example by 

clicking on a Virtual Earth map) and we want to visualize the latest weather data observed at the nearest weather 

station, avoiding the user the annoyance of the much dreaded postback.

The Solution.

Luckily, the findNearByWeatherJSON web service at Geonames does exactly what we need. It takes a longitude and 

latitude   as   parameters,   and   returns   a   JSON   object   describing   the   weather   conditions   gathered   at   the   nearest 

observing site.

Although   you  could   directly  call   this   service  from  the  client   using  classic   AJAX   patterns   (see  here  for  a   simple 

example or  have a look at ajaxpatterns), we'll focus on how to do that by using a proxy ASP.NET web service to avoid 

security problems and allow for more flexibility, and, as a nice bonus, have more readable and maintainable code. In 

fact, rather than following a RESTful approach, and call the web service directly from javascript, we'll leverage the 

ASP.NET AJAX infrastructure and use the more concise and readable RPC­style generated for us by the framework.

But the question is: how? When faced with the necessity to create a server­side proxy for a remote JSON service, 

one might be tempted to try and find something like the "Add Web Reference" wizard Visual Studio has been spoiling 

us with for years... well, no luck this time, there's nothing like that for JSON. In fact, while the wizard would help us if 

we   had   to   consume   a   SOAP   web   service,   there's   nothing   like   WSDL   which   can   describe   the   results   and   help 

generate a proxy class for JSON services.

But the good news is, we don't need any! In fact, unless you want to elaborate in some way the results returned from 

the web service (and I'll cover that topic in one of my next posts), all you need to do is create a simple local ASP.NET  

web service whose only duty, when called, is to make a request to the remote web service and forward the result to 

the client. So, let's see how to implement all this!

First of all, let's see how to implement the ASP.NET proxy web service:

using System;

using System.Web.Script.Services;

using System.Web.Services;

namespace Mashup

{
/* To allow this Web Service to be called from script using ASP.NET AJAX,

* we need to set the following attribute. */

[ScriptService]

[WebService(Namespace = "Mashup")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

public class WeatherService : WebService

[WebMethod]

public string GetWeatherByLocation(double lat, double lng)

return GeonamesWeather.GetWeatherByLocation(lat, lng);

You can see that the web service we're publishing takes two parameters, latitude and langitude of a location, and 

returns a string. We'll see that this string is going to contain exactly the content of the response received from the 

Geonames Web Service. What we have to notice now, is  that we decorated the WeatherService class with the 

[ScriptService] attribute. This is all we have to do to instruct the ASP.NET engine to create a Javascript helper class 

that will let us call this method very easily from client code.

NOTE: If you want to have a look at what the generated Javascript proxy class looks like, all you have to do

is requesting the .asmx web service from your browser, and append /js to the url. (e.g.

http://localhost:2687/JsonTest/WeatherService.asmx/js)

Now, let's delve deeper into the implementation of the GetWeatherByLocation() method:

using System;

using System.Net;

using System.Globalization;

using System.IO;

namespace Mashup

public class GeonamesWeather


{

private readonly static string FindNearbyWeatherUrl =

"http://ws.geonames.org/findNearByWeatherJSON?lat={0}&lng={1}";

public static string GetWeatherByLocation(double lat, double lng)

string formattedUri = String.Format(CultureInfo.InvariantCulture,

FindNearbyWeatherUrl, lat, lng);

HttpWebRequest webRequest = GetWebRequest(formattedUri);

HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();

string jsonResponse = string.Empty;

using (StreamReader sr = new StreamReader(response.GetResponseStream()))

jsonResponse = sr.ReadToEnd();

return jsonResponse;

private static HttpWebRequest GetWebRequest(string formattedUri)

// Create the request’s URI.

Uri serviceUri = new Uri(formattedUri, UriKind.Absolute);

// Return the HttpWebRequest.

return (HttpWebRequest)System.Net.WebRequest.Create(serviceUri);

As you can see, what the  GetWeatherByLocation()  method does is simply act as a proxy. We call the remote web 

service in a RESTful manner, retrieve the response, write it into a string and finally return the string to the caller 

method. The response returned from the findNearByWeatherJSON service will be something like this:
"{"weatherObservation":{

"clouds":"few clouds",

"weatherCondition":"n/a",

"observation":"LICC 231720Z 29009KT 9999 FEW035 SCT070 16/07 Q1009",

"windDirection":290,

"ICAO":"LICC",

"elevation":17,

"countryCode":"IT",

"lng":15.05,

"temperature":"16",

"dewPoint":"7",

"windSpeed":"09",

"humidity":55,

"stationName":"Catania / Fontanarossa",

"datetime":"2007-10-23 17:20:00",

"lat":37.4666666666667,

"hectoPascAltimeter":1009 }}"

NOTE: Carriage returns have been inserted only to improve readability, but the response stream won't

contain any.

The similarity between this string and a Javascript object is quite evident. We'll see in a moment how to leverage this ­ 

not so coincidental ­ fact.

Now, our server infrastructure is almost complete. The last step we have to take is referencing the Web Service we 

just created from the page that is actually going to use it.

To do this, we simply add a ScriptManager to the page, and add a reference to the web service in its <Services> 

section:

<asp:ScriptManager ID="ScriptManager1" runat="server">

<Services>

<asp:ServiceReference Path="~/WeatherService.asmx" />

</Services>

</asp:ScriptManager>
Now we can start to set up a very simple test page to try our flaming new web service!

For now, we'll only add a couple of textboxes to input latitude and longitude, and a button to start the Web Request. 

We'll do it AJAX style, so we'll use the HTML controls rather than their heavier ASP.NET counterparts. We'll also add 

a div where we're going to visualize the weather info retrieved from the web service. Note that we are giving id's to all 

the controls so that we can reference them from the Javascript code that we are going to write.

<label for="txtLat">Latitude:</label><input type="text" id="txtLat" />

<label for="txtLng">Longitude:</label><input type="text" id="txtLng" />

<input type="button" value="Get Weather Info" onclick="callService();" />

<div id="divResult">

</div>

Now all that's left to do is write the Javascript code to wire up everything. 

We'll start by implementing the callService() function that will be fired when clicking on the button. Please note that in 

a real scenario, clicking the button would trigger some sort of validation of the input data. We won't do any of that 

here in order to keep the code as simple and streamlined as possible.

function callService() {

var latitude = $get('txtLat').value;

var longitude = $get('txtLng').value;

Mashup.WeatherService.GetWeatherByLocation(latitude, longitude,

GetWeather_success, onFailed);

Let's see what we are doing here. First of all we are creating two variables, latitude and longitude, which contain the 

value of the two textboxes. 

NOTE: Those of you who haven't put their hands on the client side ASP.NET AJAX library yet, might wonder

what the $get function does. Actually, not much, except sparing you some keystrokes, as it is a mere

shortcut for the Javascript getElementById function. In particular, when called passing only one

parameter, the $get alias resolves to document.getElementById, while when called like this: $get(id,

element), it resolves to element.getElementById(id).


After that, we're finally ready to call our proxy service. We do that by calling the Javascript proxy method generated for 

us care of the ASP.NET AJAX framework. As you can see, we can find our WeatherService.GetWeatherByLocation() 

method inside the Mashup namespace. Please note we have to use the fully qualified name to invoke the service.

Besides the two parameters defined in the web method signature in the server side code, we see that the generated 

Javascript method takes some additional parameters: onSuccess, onFailed and userContext. As you have probably 

already realised, they take the name of two callbacks, one that will be executed upon successful completion of the 

remote method invocation, the other that will be called in the unfortunate event something went wrong. The third 

parameter, which we are not using here, would let you pass a custom user context to the callback.

Now, if everything works as expected, the GetWeather_success function will be called (asynchronously) when the 

web service response is ready, and will get as an argument the web method's return value, in our case, a string 

containing our JSON­serialized weather observation object.

Now the very last thing we need to do is deserialize such string into a real Javascript object. To do that, we might 

simply call the Javascript eval() function, as I anticipated in my previous post:

function GetWeather_success(e) {

var result = eval('(' + e + ')');

var weatherData = new Sys.StringBuilder();

var line;

for(var property in result.weatherObservation)

line = String.format("<b>{0}:</b> {1}<br/>", property,

result.weatherObservation[property]); weatherData.append(line);

$get('divResult').innerHTML = weatherData.toString();

function onFailed() {

$get('divResult').innerHTML = 'Something went terribly wrong!';

 
As   I   just   said,   when   this   function   is   called,   it   is   passed   the   web   method   response,   thus  e  contains   the   JSON 

representation of a Javascript object containing all the relevant weather data retrieved from our remote Web Service. 

As you can see, calling eval() deserializes the string and returns an object we can readily use.

From now on, what we are going to do with this native Javascript object is entirely up to us. Here, as a mere example, 

I'm enumerating all its properties and displaying them inside the div we created for this purpose.

What's   worth   noting   here   is   the   use   of   two   very   interesting   and   useful   ASP.NET   AJAX   features:   the 

Sys.StringBuilder class that mimics its server­side homonym and greatly improves string concatenation performance 

over   the   "+"   overload,   and   the  String.format()  function,   which   endows   Javascript   with   the   very   useful   .NET 

String.Format() method we are all used to.

For those of you concerned with security, as I said earlier, eval() is probably not the safest option to deserialize JSON, 

so you might decide to resort to a JSON parser (you can find one here on json.org), or once again leverage the power 

of   ASP.NET   AJAX,   by   using   the   built­in  JavaScriptSerializer.  If   you   simply   substitute   the   first   line   in 

GetWeather_success() with the following line:

var result = Sys.Serialization.JavaScriptSerializer.deserialize(e, true);

the string returned from the web method will be checked against a regular expression to ensure it is actually a valid  

JSON string.

Well, that's all for now! I hope that this short introduction to the use of JSON with ASP.NET has been useful to some 

of you!

showing current weather conditions on a map >> 

JSON in ASP.NET Ajax: Part 3. Server side deserialization and 
elaboration of JSON data returned from a Web Service. 
Posted on Wednes day, October  31, 2007 6:32 PM

This is the third part of a series of articles on ASP.NET AJAX and JSON. If you haven't read part 1 and part 2 of this  

series, you are strongly advised to do so before going on to read this article.

Let's take what we've seen so far on JSON and ASP.NET one step further, and discover how we can leverage the 

ASP.NET AJAX infastructure to make the managing of an AJAX application even sleeker. Now, let's imagine that we 

want to elaborate the JSON response returned from the web service before sending it back to the client. 
This can prove useful for many a reason, for example, to enrich the returned object with properties generated by 

complex elaborations, which we would rather do on the server than on the client.

In fact, what we will see today, is how to add a  Stability Class  property, computed used the Pasquill method for 

categorizing atmospheric turbulence, to the weather observation data returned from the Geonames web service we 

have come to know in my previous articles. I want to point out that this is merely an example of a complex elaboration 

that would generally take place on the server rather than on the client, but it could be substituted with any kind of data 

transformation or elaboration that you would need to apply to the data before sending it back to the client.

Now, in order to do this, we might modify the returned JSON object directly as a string, but you can imagine how 

cumbersome and error­prone this process would become if the elaborations to be made were less than trivial. We 

need some more flexibility. And here's where the ASP.NET AJAX infrastructure comes to help us again.

What we want to do first, is convert the JSON response to a .NET object, in order to pave the way for the elaborations 

that we are going to make. We can do this in a straightforward way, leveraging the .NET   JavaScriptSerializer

class, which can be found in the System.Web.Script.Serialization namespace. But before starting the actual 

deserialization process, we need to make some preparation. First of all, we need to create the class we want our 

JSON object to be shaped into:

public class WeatherObservation


{
public string clouds { get; set; }
public string observation { get; set; }
public string weatherCondition { get; set; }
public int windDirection { get; set; }
public int windSpeed { get; set; }
public string ICAO { get; set; }
public int elevation { get; set; }
public string countryCode { get; set; }
public double lat { get; set; }
public double lng { get; set; }
public int temperature { get; set; }
public int dewPoint { get; set; }
public int humidity { get; set; }
public string stationName { get; set; }
public string datetime { get; set; }
public int hectoPascAltimeter { get; set; }
public string stabilityClass { get; set; }
}

As you can see, the properties that we added to the class map directly to those present in the returned JSON object. 

(See here if you haven't read my previous post and you want to see how the returned JSON string looks like). Also 

notice that this is the place where we define the additional properties that we want to send to the client along with the 

original ones; in this case, we added the stabilityClass property.

NOTE: You might notice that we used Automatic Properties, a new language feature introduced with C# 3.0,

to make our code more compact and readable (let alone saving a lot of keystrokes ;) ). You can read more

on Automatic Properties on Scott Guthrie's blog. If you are not using C# 3.0 yet, the only thing you need to

do in order to make this work, is to write basic properties or public fields rather than Automatic Properties.

Actually, if you look at the returned JSON string closely, you'll find out that the weather observation is not returned as 

a top­level object, but is itself the content of the top­level weatherObservation property. This might seem redundant at 

first, but this schema is due to fact that more than one weather observation can be returned from the Web Service if 

desired, and in that case the weatherObservations property would contain an array of observations. Anyway, we can 

overlook this detail for the moment; all we need to do now to make everything work smoothly, is define a new class, 

containing a weatherObservation property:

public class SingleWeatherObservation


{
public WeatherObservation weatherObservation { get; set; }
}

Now we're ready to modify our GetWeatherByLocation method.
We'll add a call to the Deserialize<T>() method of the JavaScriptSerializer class. This method will return an 

object of type T, containing the deserialized data, which we will use for all our subsequent computations. In particular, 

we'll pass this object to our  ComputeStabilityClasses()  method, in order to compute the suggested Stability 

Classes. Later, we'll use this value to set the stabilityClass property of our object. Let's see how the code looks 

like:

public static string GetWeatherByLocation(double lat, double lng)


{
string formattedUri = String.Format(CultureInfo.InvariantCulture,
FindNearbyWeatherUrl, lat, lng);

HttpWebRequest webRequest = GetWebRequest(formattedUri);


HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse();
string jsonResponse = string.Empty;
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
jsonResponse = sr.ReadToEnd();
}

JavaScriptSerializer ser = new JavaScriptSerializer();


SingleWeatherObservation observation =
ser.Deserialize<SingleWeatherObservation>(jsonResponse);
observation.weatherObservation.stabilityClass =
ComputeStabilityClasses(observation.weatherObservation);
return ser.Serialize(observation);
}
private static string ComputeStabilityClasses(WeatherObservation weatherObservation)
{
// ... code for computing the Stability Class
}

 
As you can see, the last thing we do is serialize our object back to a JSON string before handing it back to the caller 

(which happens to be our proxy Web Service). This is perfectly legitimate, but is an unnecessary step that can be 

avoided, now that we are working with .NET objects. In fact, the AJAX framework can take the burden of serializing 

and deserializing .NET objects to and from JSON onto its shoulders, in order to make the process of calling ASP.NET 

Script­enabled Web Services completely transparent to the user. Let's see how to do this.

First of all, we need to make some little changes to our  GetWeatherByLocation()  method in order to make it 

return our .NET object rather than its JSON­serialized counterpart; we'll have to change its signature, and return our 

observation object without serializing it:

public static SingleWeatherObservation GetWeatherByLocation(double lat, double lng)


{
//.... Same code as above
return observation;
}

Then we'll have to propagate this change to the caller method, i.e. the Web Service's GetWeatherByLocation()

method:

namespace Mashup
{
/* To allow this Web Service to be called from script using ASP.NET AJAX,
* we need to set the following attribute. */
[ScriptService]
[WebService(Namespace = "Mashup")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class WeatherService : WebService
{
[WebMethod]
public SingleWeatherObservation GetWeatherByLocation(double lat, double lng)
{
return GeonamesWeather.GetWeatherByLocation(lat, lng);
}
}
}

You can see that the Web Method returns a  SingleWeatherObservation object rather than a string containing 

its JSON representation. Now, we'll have to make some little changes to the client code in order to reflect the changes 

made to the server side.

function callService()
{
var latitude = $get('txtLat').value;
var longitude = $get('txtLng').value;
Mashup.WeatherService.GetWeatherByLocation(latitude, longitude,
GetWeather_success, onFailed);
}
function GetWeather_success(e)
{
//var result = Sys.Serialization.JavaScriptSerializer.deserialize(e, true);
var result = e;
var weatherData = new Sys.StringBuilder();
var line;
for(var property in result.weatherObservation)
{
line = String.format("<b>{0}:</b> {1}<br/>",
property, result.weatherObservation[property]);
weatherData.append(line);
}
$get('divResult').innerHTML = weatherData.toString();
}

 
Nothing has changed from what we have seen previously, except for the first line in GetWeather_success(e); we 

don't need to explicitly deserialize the argument received from the Web Service call anymore, as this is done under 

the hood for us by the AJAX framework. We can assume that  e is a Javascript object that closely mimics the structure 

of the .NET object returned from the Web Method. 

Notice that by default, JavaScriptSerializer supports most usual data types. However, you can serialize

and deserialize types that are not natively supported by JavaScriptSerializer by implementing custom

converters using the JavaScriptConverter class, and then register the converters by using the

RegisterConverters method. For more information, consult this page of the ASP.NET AJAX
Documentation, or have a look at this interesting article by Chris Pietschmann

As you can see, we have attained a completely transparent communication between server and client code. All the 

details of serialization / deserialization to and from JSON are hidden from our view by the ASP.NET AJAX framework.

Das könnte Ihnen auch gefallen