Current Articles | RSS Feed
Modern web applications that make heavy use of Ajax benefit from mashing up data from different services. Applications built with the Google Web Toolkit (GWT) can work with both XML and JSON data. In this two-part series, we'll build a project to show how to get and process XML and JSON data, and deal with sundry matters such as security restrictions and server-side proxies. What you'll learn here should help you deal with all kinds of services and enhance your GWT applications.
In order to profit from this article, you need to have a working knowledge of GWT, the basics of XML, and some JavaScript practice.
To show how to process XML and JSON services, we first need a simple API that can deliver data in both these formats. The Google Geocoding API fits the bill. Geocoding is a process that can derive geographic coordinates (usually latitude and longitude) from an address such as "10901 W 120th Ave, Broomfield, CO." (There's also a reverse geocoding service to turn coordinates into an address, but we won't be using it.) You can then use the returned data to show a marker on a map or for navigation purposes. We'll use the current version 3 of this API here, but we'll also use V2 for our JSONP examples in part 2 of this series.To show what you can do with GWT, we'll write a simple application, based on a form where a user can enter an address to look up, a couple of output fields for the geographic coordinates of the place, and a text area for some extra information about the found site. (See figure below.) Check the source code for details, but building the form is straightforward. It's based on a HTML simple design to which widgets are added through GWT.[caption id="attachment_92363" align="alignnone" width="300" caption="Our basic form, based on straightforward GWT"][/caption]We'll use three ways of getting geocoding data from the remote server; each of the three buttons will invoke a different one. If the API call happens to fail, the application will give a warning. On success, it will show the latitude and longitude of the address and some extra information.
ServerProxy
getFromRemoteServer(…)
postToRemoteServer(…)
package com.google.gwt.kereki.xmlJsonTest.client;import com.google.gwt.kereki.xmlJsonTest.shared.ServerProxyException;import com.google.gwt.user.client.rpc.RemoteService;import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;@RemoteServiceRelativePath("serverproxy")public interface ServerProxy extends RemoteService { public String getFromRemoteServer(final String serviceUrl) throws ServerProxyException; public String postToRemoteServer(final String serviceUrl) throws ServerProxyException;}
ServerProxyAsync
AsyncCallback<String>
package com.google.gwt.kereki.xmlJsonTest.client;import com.google.gwt.user.client.rpc.AsyncCallback;public interface ServerProxyAsync { void getFromRemoteServer( final String serviceUrl, AsyncCallback<String> callback); void postToRemoteServer( final String serviceUrl, AsyncCallback<String> callback);}
ServerProxyImpl
ServerProxyException
extends Exception
package com.google.gwt.kereki.xmlJsonTest.server;...several import lines...@SuppressWarnings("serial")public class ServerProxyImpl extends RemoteServiceServlet implements com.google.gwt.kereki.xmlJsonTest.client.ServerProxy { @Override public String getFromRemoteServer(final String serviceUrl) throws ServerProxyException { String result= ""; try { final URL url= new URL(serviceUrl); final BufferedReader in= new BufferedReader(new InputStreamReader( url.openStream())); String inputLine; while ((inputLine= in.readLine()) != null) { result+= inputLine; } in.close(); return result; } catch (final Exception e) { throw new ServerProxyException(); } } @Override public String postToRemoteServer(final String serviceUrl) throws ServerProxyException { // // we won't be using this code // see its implementation in the provided source files // }}
http://maps.googleapis.com/maps/api/geocode/xml?address=address.to.be.geocoded&sensor=false
result
<?xml version="1.0" encoding="UTF-8"?><GeocodeResponse> <status>OK</status> <result> <type>street_address</type> <formatted_address>10901 W 120th Ave, Broomfield, CO 80021, USA</formatted_address> <address_component> <long_name>10901</long_name> <short_name>10901</short_name> <type>street_number</type> </address_component> <address_component> <long_name>W 120th Ave</long_name> <short_name>W 120th Ave</short_name> <type>route</type> </address_component> ...several lines snipped... <address_component> <long_name>80021</long_name> <short_name>80021</short_name> <type>postal_code</type> </address_component> <geometry> <location> <lat>39.9159880</lat> <lng>-105.1178850</lng> </location> <location_type>ROOFTOP</location_type> <viewport><em>...several lines snipped...</em></viewport> </geometry> </result></GeocodeResponse>
useless
unneeded
<location>
while
if (inputLine.trim().equals("<location>")) { inputLine= "<location useless='" + (new BigInteger(48, new Random()).toString(32)) + "' unneeded='" + (new BigInteger(48, new Random())) + "'>";}
private final ServerProxyAsync serverProxy= GWT.create(ServerProxy.class);getXmlFromServerButton.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { serverProxy.getFromRemoteServer( "http://maps.googleapis.com/maps/api/geocode/xml?address=" + URL.encode(addressField.getText()) + "&sensor=false", new AsyncCallback<String>() { @Override public void onFailure(Throwable caught) { Window.alert("Failure getting XML through proxy"); } @Override public void onSuccess(String result) { processXml(result); } }); } });
serverProxy
AsyncCallback
processXml(…)
URL.encode(…)
<inherits name="com.google.gwt.xml.XML"/>
public void processXml(final String xml) { final Document xmlDoc= XMLParser.parse(xml); final Element root= xmlDoc.getDocumentElement(); XMLParser.removeWhitespace(xmlDoc); final NodeList results= root.getElementsByTagName("result"); final Element firstResult= (Element) results.item(0); final NodeList addressParts= firstResult .getElementsByTagName("address_component"); final Element geometry= (Element) firstResult.getElementsByTagName( "geometry").item(0); final Element location= (Element) geometry.getElementsByTagName( "location").item(0); final String latitude= location.getElementsByTagName("lat").item(0) .getFirstChild().getNodeValue(); final String longitude= location.getElementsByTagName("lng").item(0) .getFirstChild().getNodeValue(); latitudeField.setText(latitude); longitudeField.setText(longitude); String detailedAddress= ""; /* * The following is just to show how to get the values of the invented * "useless" and "unneeded" attributes of the <location> element */ detailedAddress= "USELESS=" + location.getAttribute("useless") + "" + "UNNEEDED=" + location.getAttribute("unneeded") + ""; /* * Let's build up the address description by joining all the (possibly * more than one) <type> and (only one) <long_name> values */ for (int i= 0; i < addressParts.getLength(); i++) { Element components= (Element) addressParts.item(i); /* * Loop over all the <type> nodes */ String separator= ""; NodeList types= components.getElementsByTagName("type"); for (int j= 0; j < types.getLength(); j++) { detailedAddress= detailedAddress + separator + types.item(j).getFirstChild().getNodeValue(); separator= ", "; } /* * Add the <long_name> node value */ detailedAddress= detailedAddress + ": " + components.getElementsByTagName("long_name").item(0) .getFirstChild().getNodeValue() + ""; } fullAddressField.setText(detailedAddress);}
Document
getDocumentElement(…)
<GeoResponse>
removeWhiteSpace(…)
<result>
getElementsByTagName(…)
item(0)
getElementsByTagName(…).item(0)
geometry
location
getFirstChild().getNodeValue()
latitude
longitude
getLength(…)
type
addressParts
getAttribute(…)
hasAttributes(…)
getAttributes(…)
getFirstChild(…)
getLastChild(…)
getChildNodes(…)
getNextSibling(…)
getPreviousSibling(…)
Allowed tags: <a> link, <b> bold, <i> italics