provides software and services that enable enterprises
Live Chat 1-888-673-6564

Open Source Software Technical Articles

  • Home
  • Search
  • Contact Us
  • Products and Support
  • Services
  • Enterprise OSS Blog
  • Wazi Technical Blog
  • About Wazi
  • Attributions and Licensing
  • Supply Chain Compliance
  • How to Contribute
  • Contributors
  • Resources Library
  • Cloud Services
  • Partners
  • Customers
  • Community
  • Company
  • Careers
  • News and Events

Subscribe to Wazi by Email

Your email:


Enterprise Developer Support 24 x 7, Get a Support Quote Now!


click-here-to-chat-with-an-online-representative

download-oss-discovery

Latest Posts

  • The secret to great reporting with Drupal 7
  • A more colorful LibreOffice unveiled
  • Toward a more colorful LibreOffice
  • Flexible administration with Puppet's Facter and templates
  • Knock for OpenSSH
  • Get more out of phpMyAdmin
  • Image annotation in GIMP, Dia, and OpenOffice Draw
  • Solr, Drupal 7, and faceted search
  • Using FreeNAS' new full disk encryption for ZFS
  • Create distributed storage with Gluster

Connect with Us!

Current Articles | RSS Feed RSS Feed

Google Web Toolkit and Web Services: The JSON/JSONP Way

Posted by Federico Kereki on Mon, Nov 07, 2011
  
Email This Email Article  
Tweet  
  

In the first part of this series, we studied how to implement and use a server-side proxy to contact a remote service, and how to use the Google Web Toolkit (GWT) to process the XML results from the Google Geocoding API. For many applications, however, JSON (JavaScript Object Notation) is an even better fit than XML for GWT, since it can be easily processed with JavaScript. Let's see how to use this alternative format, and, by the way, discover how to do so without any server-side proxies by means of JSON with Padding (JSONP), an interesting extension of the JSON concept.


Since version 2.0, GWT has provided several classes and methods that make it simple to process JSON. First we'll build on the code we created in the first article in this series, using the same proxy, then we'll move over to JSONP, which will let us directly access the service from our client application.



Let's start by analyzing the results from the API. The required URL is practically the same as in the XML code; we just have to substitute json for xml; thus http://maps.googleapis.com/maps/geo?output=json&q=10901%20W%20120th%20Ave,Broomfield,CO&sensor=false produces a result as seen below.
{
"results" : [
{
"address_components" : [
{
"long_name" : "10901",
"short_name" : "10901",
"types" : [ "street_number" ]
},
{
"long_name" : "W 120th Ave",
"short_name" : "W 120th Ave",
"types" : [ "route" ]
},
{
"long_name" : "Broomfield",
"short_name" : "Broomfield",
"types" : [ "locality", "political" ]
},

...several snipped lines...

{
"long_name" : "80021",
"short_name" : "80021",
"types" : [ "postal_code" ]
}
],
"formatted_address" : "10901 W 120th Ave, Broomfield, CO 80021, USA",
"geometry" :
{
"location" :
{
"lat" : 39.9159880,
"lng" : -105.1178850
},
"location_type" : "ROOFTOP",
"viewport" :
{
...several snipped lines...
}
},
"types" : [ "street_address" ]
}
],
"status" : "OK"
}

This output is similar to the XML version we saw in the first article. Instead of tags, braces surround objects, and repeated tags are represented by bracketed arrays. We don't have to fake attributes (as we did in the XML case) because all values are attributes here. We can get this JSON string by using the server-side proxy, so that part of the problem is already solved; see the listing below.
private final ServerProxyAsync serverProxy= GWT.create(ServerProxy.class);

getJsonFromServerButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
serverProxy.getFromRemoteServer(
"http://maps.googleapis.com/maps/api/geocode/json?address="
+ URL.encode(addressField.getText()) + "&sensor=false",

new AsyncCallback<String>() {
@Override
public void onFailure(Throwable caught) {
Window.alert("Failure getting JSON through proxy");
}

@Override
public void onSuccess(String result) {
processJson(result);
}
});
}
});

Running this code (we'll get to the processJson(…) method in a moment) produces results very much like the XML ones (no surprise) but before getting to the actual process, let's take a brief detour to study the way GWT provides for easy access to JSON objects.

Record Overlays


How do we convert the received string into an object we can work with? A single line is enough: JsonUtils.unsafeEval(...) basically just calls the eval(...) function to do the required conversion. (The "unsafe" part of the method name indicates that there are no safety measures; if you were to receive rogue code, it would be executed without a whisper.) To deal with the JSON object in Java, we can use another GWT feature, record overlays, which allow us to use the JavaScript object via getters and setters as usual, as if it were a standard, run-of-the-mill Java object. Bear with me, and let's first analyze the process code to study how we'll be using the object.
public void processJson(final String json) {
final GeoDataV3 gd3= JsonUtils.unsafeEval(json);

latitudeField.setText("" + gd3.getLatitude());
longitudeField.setText("" + gd3.getLongitude());

String detailedAddress= "";

for (int i= 0; i < gd3.getAddressComponentsLength(); i++) {
String separator= "";

for (int j= 0; j < gd3.getAddressComponentTypeLength(i); j++) {
detailedAddress= detailedAddress + separator
+ gd3.getAddressComponentType(i, j);
separator= ", ";
}

detailedAddress= detailedAddress + ": "
+ gd3.getAddressPartsLongName(i) + "
";
}
fullAddressField.setText(detailedAddress);
}

The first line produces a GeoDataV3 object, which we'll be seeing in a moment, with getters such as getLatitude(…) and getLongitude(…), and even more interesting methods such as getAddressComponentsLength(…) to get how many AddressComponents there are, or getAddressComponentType(…) to get a specific type. All in all, the code is similar to the XML version, and the results of running the code are similar to what we saw earlier with XML; see below.

[caption id="attachment_92553" align="alignnone" width="300" caption="The results of processing the JSON version of the Google Geocoding API results"][/caption]

As for the object itself, the GeoDataV3 extends the JavaScriptObject class, and accesses its parts through native methods. (See listing below.) As with XML, we return data just from the first result, but you could change that. Be careful writing this sort of method; if you mess up, you'll just get a runtime error, with no information whatsoever you can use for debugging, because it's pure JavaScript.
package com.google.gwt.kereki.xmlJsonTest.client;

import com.google.gwt.core.client.JavaScriptObject;

public class GeoDataV3 extends JavaScriptObject {
protected GeoDataV3() {
}

public final native String getAddressPartsLongName(final int i)
/*-{
return this.results[0].address_components[i].long_name;
}-*/;

public final native int getAddressComponentsLength()
/*-{
return this.results[0].address_components.length;
}-*/;

public final native String getAddressComponentType(final int i, final int j)
/*-{
return this.results[0].address_components[i].types[j];
}-*/;

public final native int getAddressComponentTypeLength(final int i)
/*-{
return this.results[0].address_components[i].types.length;
}-*/;

public final native float getLatitude()
/*-{
return this.results[0].geometry.location.lat;
}-*/;

public final native float getLongitude()
/*-{
return this.results[0].geometry.location.lng;
}-*/;
}

This is Java code, but actually it's mostly JavaScript. Working with JSON makes you get closer to JavaScript; with XML, you could do with pure Java code. Picking XML or JSON (should both be available, as with the Google Geocoding API) could be seen as a matter of personal preference, and the performance differences are way too small as to decide the matter. However, JSON services can provide extra functionality in the form of JSONP calls that allow us to directly connect the client to the service without any proxies.

JSONP: The Way To Proxy-less Calls


JSONP: The ObjectionsWhile JSONP offers useful functionality, you may want to consider some disadvantages too before using JSONP. First, and most seriously, it's sort of "loose" insofar as your browser code will accept and execute any arbitrary JavaScript that might come in the response. Thus if your application depends on JSONP to avoid SOP restrictions, you are opening up to all kinds of "dirty tricks." For example, a rogue web service could return a seemingly valid function call (as expected) but tag along some extra JavaScript logic that could hack your page, get some kind of private user data, and send it somewhere else. You can only be safe if you completely trust the other server; should it be hacked, your application would immediately be at risk.

A second, lesser, objection is that JSONP is completely tied to JavaScript, and cannot work in any other context. This isn't the same as JSON, which can be processed, for example, by PHP. JSONP is thus more restrictive than, say, XML, which is language-agnostic. If you want to use JSONP between servers, it won't do; JSONP is only geared to web browsers.

Some authors recommend preprocessing JSONP output through a server-side proxy – but that defeats its advantages! Others suggest some tricks to sandbox JSONP calls.

To wrap it up: JSONP can be dangerous, as any other piece of code that depends on other, foreign services to do its work. Consider its risks, as you would consider potential security loopholes in every other part of your system.

Up to now, every communication between our client and the remote service had to go through the server-side proxy because of the Same Origin Policy (SOP) restriction. However, there's a way out if the remote service provides JSONP (JSON with Padding) calls. A JSONP call, instead of just providing a JSON string, includes a call to a function of your own, which is executed when the JSON data arrives. Ajax cannot be used to invoke a remote service, but you can load remote JavaScript from another server, so JSONP provides the desired solution – and GWT very conveniently allows you to use it easily.
19a98812-f823-48dc-841e-bf029c63c6d7

We have just a small problem here: the current version (V3) of the Google Geocoding API doesn't provide JSONP. (You can only use JSONP if the remote service "knows" about it, and provides its result data in the correct format, including the function call.) However, the previous version (V2) of the API did provide JSONP; even if it's currently deprecated, for the time being it will do for our examples. The URL to get is similar, http://maps.googleapis.com/maps/geo?output=json&q=10901%20W%20120th%20Ave,Broomfield,CO&sensor=false&callback=yourcode; just note the extra parameter, specifying we want the server to include a call to the yourcode(…) function.

The results of the call are below. Note that the JSON format is different; this is not because of JSONP, but rather because of the earlier API version's way of providing results.
yourcode && yourcode({
"name": "10901 W 120th Ave,Broomfield,CO",
"Status": {
"code": 200,
"request": "geocode"
},
"Placemark": [ {
"id": "p1",
"address": "10901 W 120th Ave, Broomfield, CO 80021, USA",
"AddressDetails": {
"Accuracy" : 8,
"Country" : {
"AdministrativeArea" : {
"AdministrativeAreaName" : "CO",
"Locality" : {
"LocalityName" : "Broomfield",
"PostalCode" : {
"PostalCodeNumber" : "80021"
},
"Thoroughfare" : {
"ThoroughfareName" : "10901 W 120th Ave"
}
}
},
"CountryName" : "USA",
"CountryNameCode" : "US"
}
},
"ExtendedData": {
"LatLonBox": {
...some lines snipped out...
}
},
"Point": {
"coordinates": [ -105.1178850, 39.9159880, 0 ]
}
}]
})

As we said, GWT greatly simplifies doing JSONP calls. You can simply use the JsonpRequestBuilder class, as shown below. You need not care about creating or specifying the callback function; GWT will add the callback= parameter on its own to the service URL, and of course create the required function. On the other hand, should the remote service require a different name for the parameter, you could use setCallbackParam(…) to send something other than callback.
getJsonDirectlyButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
final JsonpRequestBuilder jsonprb= new JsonpRequestBuilder();
jsonprb.requestObject(
"http://maps.googleapis.com/maps/geo?output=json&q="
+ URL.encode(addressField.getText()) + "&sensor=false",

new AsyncCallback() {
@Override
public void onFailure(Throwable caught) {
Window
.alert("Failure getting JSONP directly from remote server");

}

@Override
public void onSuccess(GeoDataV2 result) {
processJsonp(result);
}
});
}
});

Given the differences in format, we'll require another record overlay; let's call it GeoDataV2. We'll do a simpler job this time, and provide access to fewer attributes.
package com.google.gwt.kereki.xmlJsonTest.client;

import com.google.gwt.core.client.JavaScriptObject;

public class GeoDataV2 extends JavaScriptObject {
protected GeoDataV2() {
}

public final native String getAddress()
/*-{
return this.Placemark[0].address;
}-*/;

public final native String getLocality()
/*-{
return this.Placemark[0].AddressDetails.Country.AdministrativeArea.Locality.LocalityName;
}-*/;

public final native String getPostalCode()
/*-{
return this.Placemark[0].AddressDetails.Country.AdministrativeArea.Locality.PostalCode.PostalCodeNumber;
}-*/;

public final native float getLatitude()
/*-{
return this.Placemark[0].Point.coordinates[1];
}-*/;

public final native float getLongitude()
/*-{
return this.Placemark[0].Point.coordinates[0];
}-*/;
}

With this record overlay, the required processing code is simply as shown below.
public void processJsonp(final GeoDataV2 gd2) {
fullAddressField.setText(gd2.getAddress() + "
Locality:"
+ gd2.getLocality() + "
Postal Code:" + gd2.getPostalCode());

latitudeField.setText("" + gd2.getLatitude());
longitudeField.setText("" + gd2.getLongitude());
}

The results of running this code are as follows.

[caption id="attachment_92554" align="alignnone" width="300" caption="The results of the JSONP data, provided by a different version of the Google Geocoding API"][/caption]

 

In Conclusion


In this second article in this series, we have seen how to process JSON code, and even how to avoid using any proxies by using JSONP calls. Using data from remote web services is common in modern web aplications, and GWT provides simple tools for this task, allowing you to use well-known standards such as XML and JSON. By using the techniques shown here, you can make your applications even more powerful. Go ahead and start mashing data up, for a better end-user experience!
Follow @openlogic
Follow @CloudSwing

This work is licensed under a Creative Commons Attribution 3.0 Unported License
Creative Commons License.Follow @openlogic
Follow @OSCloudServices

This work is licensed under a Creative Commons Attribution 3.0 Unported License
Creative Commons License.
Tags: Technical, Tips & Tricks, Web Server, JSON, GWT

Comments

Currently, there are no comments. Be the first to post one!
Post Comment
Name
 *
Email
 *
Website (optional)
Comment
 *

Allowed tags: <a> link, <b> bold, <i> italics

Loading...
Error sending email
Email sent successfully

Email article
Email To : 
Your name : 
Message : (maximum 200 characters)
Home | Search | Contact Us | Products and Support | Services | Enterprise OSS Blog | Wazi Technical Blog | Resources Library | Cloud Services | Partners | Customers | Community | Company | Careers | News and Events
Products
OpenLogic Exchange (OLEX)
License Compliance Module
OSS Discovery
OSS Deep Discovery
OpenUpdate
Services
Open Source Support
CentOS Support
Scanning & Compliance
Open Source Training
Professional Services
Solutions
Support & Indemnification
Open Source Governance
Open Source Scanning
Open Source Provisioning
Consulting & Training
Contact Us
1-888-673-6564


© 2013 OpenLogic, Inc. All rights reserved.
Site Map  |  Privacy Policy