- XML Reference Guide
- Overview
- What Is XML?
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Table of Contents
- The Document Object Model
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- DOM and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- Implementations
- DOM and JavaScript
- Using a Repeater
- Repeaters and XML
- Repeater Resources
- DOM and .NET
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Downloads
- DOM and C++
- DOM and C++ Resources
- DOM and Perl
- DOM and Perl Resources
- DOM and PHP
- DOM and PHP Resources
- DOM Level 3
- DOM Level 3 Core
- DOM Level 3 Load and Save
- DOM Level 3 XPath
- DOM Level 3 Validation
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Implementations
- The Simple API for XML (SAX)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- SAX and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- SAX and .NET
- Informit Articles and Sample Chapters
- SAX and Perl
- SAX and Perl Resources
- SAX and PHP
- SAX and PHP Resources
- Validation
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Document Type Definitions (DTDs)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XML Schemas
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- RELAX NG
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Schematron
- Official Documentation and Implementations
- Validation in Applications
- Informit Articles and Sample Chapters
- Books and e-Books
- XSL Transformations (XSLT)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XSLT in Java
- Java in XSLT Resources
- XSLT and RSS in .NET
- XSLT and RSS in .NET Resources
- XSL-FO
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XPath
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XML Base
- Informit Articles and Sample Chapters
- Official Documentation
- XHTML
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XHTML 2.0
- Documentation
- Cascading Style Sheets
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XUL
- XUL References
- XML Events
- XML Events Resources
- XML Data Binding
- Informit Articles and Sample Chapters
- Books and e-Books
- Specifications
- Implementations
- XML and Databases
- Informit Articles and Sample Chapters
- Books and e-Books
- Online Resources
- Official Documentation
- SQL Server and FOR XML
- Informit Articles and Sample Chapters
- Books and e-Books
- Documentation and Implementations
- Service Oriented Architecture
- Web Services
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Creating a Perl Web Service Client
- SOAP::Lite
- Amazon Web Services
- Creating the Movable Type Plug-in
- Perl, Amazon, and Movable Type Resources
- Apache Axis2
- REST
- REST Resources
- SOAP
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- SOAP and Java
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- WSDL
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- UDDI
- UDDI Resources
- XML-RPC
- XML-RPC in PHP
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Ajax
- Asynchronous Javascript
- Client-side XSLT
- SAJAX and PHP
- Ajax Resources
- JSON
- Ruby on Rails
- Creating Objects
- Ruby Basics: Arrays and Other Sundry Bits
- Ruby Basics: Iterators and Persistence
- Starting on the Rails
- Rails and Databases
- Rails: Ajax and Partials
- Rails Resources
- Web Services Security
- Web Services Security Resources
- SAML
- Informit Articles and Sample Chapters
- Books and e-Books
- Specification and Implementation
- XML Digital Signatures
- XML Digital Signatures Resources
- XML Key Management Services
- Resources for XML Key Management Services
- Internationalization
- Resources
- Grid Computing
- Grid Resources
- Web Services Resource Framework
- Web Services Resource Framework Resources
- WS-Addressing
- WS-Addressing Resources
- WS-Notifications
- New Languages: XML in Use
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Google Web Toolkit
- GWT Basic Interactivity
- Google Sitemaps
- Google Sitemaps Resources
- Accessibility
- Web Accessibility
- XML Accessibility
- Accessibility Resources
- The Semantic Web
- Defining a New Ontology
- OWL: Web Ontology Language
- Semantic Web Resources
- Google Base
- Microformats
- StructuredBlogging
- Live Clipboard
- WML
- XHTML-MP
- WML Resources
- Google Web Services
- Google Web Services API
- Google Web Services Resources
- The Yahoo! Web Services Interface
- Yahoo! Web Services and PHP
- Yahoo! Web Services Resources
- eBay REST API
- WordML
- WordML Part 2: Lists
- WordML Part 3: Tables
- WordML Resources
- DocBook
- Articles
- Books and e-Books
- Official Documentation and Implementations
- XML Query
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- XForms
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Resource Description Framework (RDF)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Topic Maps
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation, Implementations, and Other Resources
- Rich Site Summary (RSS)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- Simple Sharing Extensions (SSE)
- Atom
- Podcasting
- Podcasting Resources
- Scalable Vector Graphics (SVG)
- Informit Articles and Sample Chapters
- Books and e-Books
- Official Documentation
- OPML
- OPML Resources
- Summary
- Projects
- JavaScript TimeTracker: JSON and PHP
- The Javascript Timetracker
- Refactoring to Javascript Objects
- Creating the Yahoo! Widget
- Web Mashup
- Google Maps
- Indeed Mashup
- Mashup Part 3: Putting It All Together
- Additional Resources
- Frequently Asked Questions About XML
- What's XML, and why should I use it?
- What's a well-formed document?
- What's the difference between XML and HTML?
- What's the difference between HTML and XHTML?
- Can I use XML in a browser?
- Should I use elements or attributes for my document?
- What's a namespace?
- Where can I get an XML parser?
- What's the difference between a well-formed document and a valid document?
- What's a validating parser?
- Should I use DOM or SAX for my application?
- How can I stop a SAX parser before it has parsed the entire document?
- 2005 Predictions
- 2006 Predictions
- Nick's Book Picks
At this point, we've accomplished the first step in our mashup project, which was to create an application out of a web API. Of course, the application itself isn't terribly useful; all we can really do is display a map and add markers to it. We can also center the map, but it's a rather arbitrary process, as we're just centering on the location in which the user clicks. What we're going to do now is look at a second API, which we will ultimately combine with the first for our mashup. (In fact, we are going to integrate a third service as well, but we'll get to that in the next section.)
The second service we're going to integrate is itself a combination of different services. The Indeed job search site includes listings from many different sources, such as Dice.com, Monster.com, and even classified ads. You can go to their site and do a search, or you can use their web API, as we are going to do.
The Indeed API requires you to have a "key", which entitles you to 500 free searches a day. I've included my key in the application; if you have trouble using it, go to the site and get your own key. It's free, and you just have to plug it into the form.
The API uses REST, which means it's going to be extremely simple for us to use the data with Ajax. What we're going to do is use a form to collect the information, use the information from the form to create the URL to which were going to submit the request, receive the information, and then analyze and display it in a div
. Later, we'll plot it out on the map.
The first step is to create the actual form:
<html> <head><title>Indeed Jobs</title></head> <body> <div id="jobs"></div> <script src="https://maps.google.com/maps?file=api&v=1&key=ABQIAAAAZBHmEX-pUz26mphjVq6DsBTENWKvAmocgv4W7AlW6y4wknzddxSNvlGbpUPwhclnFklpZ8Ty4jkvSw" type="text/javascript"></script> <script type="text/javascript"> </script> <h1>Find a job</h1> <form action="" onsubmit="sendRequest(this); return false;"> <table> <tr><td>Indeed key:</td><td><input type="text" name="key" value="8ddefca067a351d2f4dd3db4c2d90ce5" /></td></tr> <tr><td>What kind of job:</td><td><input type="text" name="q" value="" /></td></tr> <tr><td>Where you want it:</td><td><input type="text" name="l" value="" /></td></tr> <tr><td colspan="2"><input type="submit" value="Send" /></td></tr> </table> </form> </body> </html>
You may have noticed that we are including the script that contains the Google Maps API. This is not a mistake. In fact, as I mentioned earlier, the API includes several pieces that are not necessarily visual, but are extremely handy. One of those is a version of the XmlHttpRequest
object, which is going to vastly simplify our lives when we start creating Ajax requests.
In the meantime, notice that we have a generic form, with the exception of the fact that rather than submitting it to an actual URL, the browser executes the sendRequest()
script. By returning false
for the onsubmit
attribute, we guarantee that the form itself will never actually be submitted, so the user remains on this page.
The next step is to build the request:
... <script type="text/javascript"> function sendRequest(theForm){ document.getElementById("jobs").innerHTML = "Sending..."; var url = "http://api.indeed.com/apisearch?"; url = url + "key="+URLencode(theForm.elements["key"].value); url = url + "&q="+URLencode(theForm.elements["q"].value); url = url + "&l="+URLencode(theForm.elements["l"].value); url = url + "&filter=1&latlong=1"; getJobs(url); } function URLencode(sStr) { return escape(sStr).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F'); } </script> <h1>Find a job</h1> <form action="" onsubmit="sendRequest(this); return false;"> ...
The process of building the URL is pretty straightforward. All we're doing here is pulling out specific values from the form, and using the URLencode
function we first used with our timesheet project to make sure the data gets passed properly. The last two parameters are needed by the Indeed API to filter out duplicate results, and to tell the system to return a latitude and longitude value for each job. Remember, Google Maps only works with these coordinates, as opposed to street addresses.
If you have difficulty getting latitude and longitude values with a request like this, try using a value of true
rather than a value of 1
. During the writing of this article, the value did change from one to the other and back again, even though the documentation specifies a value of 1
. Also, the request will return a maximum of 10 jobs, but if you like, you can create requests that start on subsequent "pages", say with a "next" and "previous" link, by adding the start
value and specifying the number of records to offset.
Finally, we'll call the function that actually requests the jobs from the server.
Now, before we can move on, and we do need to once again visit one of the problems of using Ajax in the browser: cross domain restrictions. As before, we are running up against the problem that a browser may not request an HTTP stream from any server but the one from which the page was downloaded. So to solve that problem, we will once again use our proxy script:
<? $filepath = $_GET['url']; $file_contents = file_get_contents($filepath); print($file_contents); ?>
This PHP script doesn't actually do anything major. All it does is retrieve the contents of any the request, and then print it back out to the browser, or in this case, to the JavaScript request. Save it on your server as proxy.php
. (Feel free to implement this in your own favorite language, of course! I've set up a page for different versions of this proxy script. Feel free to check for your language, or contribute your own!)
All of this means that if we have for for which we create the Indeed URL of:
http://api.indeed.com/apisearch?key=8ddefca067a351d2f4dd3db4c2d90ce5&q=java%20developer&l=32618&filter=1&latlong=1
We'll wind up with the result something like:
<?xml version="1.0" encoding="UTF-8" ?> <response> <query>java developer</query> <location>32618</location> <dupefilter>true</dupefilter> <highlight>true</highlight> <totalresults>7</totalresults> <start>1</start> <end>7</end> <results> <result> <jobtitle>Web Application Developer/.Net Programmer</jobtitle> <company>Netsource Technologies</company> <city>Ocala</city> <state>FL</state> <country>US</country> <source>Monster</source> <date>Sat, 18 Mar 2006 02:08:22 GMT</date> <snippet><b>Developer</b>/.Net Programmer for our Ocala office. We are not looking for contractors, outsourced <b>developers</b>... Experience: HTML, <b>Java</b> Script, and VB Script...</snippet> <url>http://www.indeed.com/rc/clk?cd=2e3Dk3g7xxVxZ0mN_gBdQvskd-U__xb7ROgfdt02u6F9tntlrf1LYeq55qzkHOByiPAM3dOdPcuZWEFR7Ft0xfGZ_aFA5TL5Ns3aCDfaoRxfzCp2kgBRgLA-pHxERbd4&qd=RnZhMybXSk4M3QtTVGXWoaz1wh_2ttdZVIqQ7WHhgHEIK4tQ9bdscM__Azf7N3BcSkE8i4JV7vE0q94XalpwDFbAR4cH_XVRVgWS9PV3xEE&rd=i9i5wxAjTH4MK5Gz0qo8FA</url> <latitude>29.187704</latitude> <longitude>-82.130615</longitude> </result> <result> <jobtitle>Web Application Developer/.Net Programmer</jobtitle> <company>One Stop</company> <city>Ocala</city> <state>FL</state> <country>US</country> <source>One Stop</source> <date>Wed, 15 Mar 2006 23:07:49 GMT</date> <snippet><b>Developer</b>/.Net Programmer for our Ocala office. We are not looking for contractors, outsourced <b>developers</b>... Experience: . HTML, <b>Java</b> Script, and VB Script...</snippet> <url>http://www.indeed.com/rc/clk?cd=YgHkxiP8EhstZuuDZHgl0PZ4GY_b826Pi2c0dt_079JoCYFfud4jUNXYOUmh99KuT3eEURrr2CzhRTdQEkPnV74YFkgGEJJnrHTdqupvW3T7MqfLqJIDFUzCTAU2bNyx8S6cmrvpP58uRPKGeE98JXmCIIh4M09Ddxc5x--uIgrBYVE3QZKHduudyQ-u1cS0yg0GT77yBHoK-FGuXzfAdQ&qd=RnZhMybXSk4M3QtTVGXWoaz1wh_2ttdZVIqQ7WHhgHEIK4tQ9bdscM__Azf7N3BcSkE8i4JV7vE0q94XalpwDFbAR4cH_XVRVgWS9PV3xEE&rd=TEF42SAR5bfxLIqpAiYczw</url> <latitude>29.187704</latitude> <longitude>-82.130615</longitude> </result> ... </results> </response>
Now let's look at actually retrieving the data:
... getJobs(url); } function getJobs(theUrl){ document.getElementById("jobs").innerHTML = "Sending..."; var request = GXmlHttp.create(); request.open('GET', 'proxy.php?url='+URLencode(theUrl), true); request.send(null); } function URLencode(sStr) { return escape(sStr).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F'); } ...
Once we set the content of the jobs
div
so the user knows something is actually happening, the first step is to create an instance of the GXmlHttp
object. This is from the Google Maps API, and is actually a cross-browser version of the XmlHttpRequest
object. (If you remember from our discussion on Ajax, we normally would have to create the object differently depending on what browser we were using.) Once we have the object, we can open the request, feeding it the URL for the request itself, which is actually the proxy and a URL encoded version of the Indeed API request. We can then send that request. Since we are using get
, all of the information is in the URL, so there is no data in the actual request.
Of course, right now we are not actually doing anything with the data when we get it.
In order to do something with the data, we have to know when it arrives. We can do that by checking the "ready state" when it changes:
... getJobs(url); } function getJobs(theUrl){ document.getElementById("jobs").innerHTML = "Sending..."; var request = GXmlHttp.create(); request.open('GET', 'proxy.php?url='+URLencode(theUrl), true); request.onreadystatechange = function() { if (request.readyState == 4) { //DO SOMETHING } }; request.send(null); } ...
In some cases, we build applications that give the user progress reports, but because we've already adjusted the results div
to show that we are doing something, we'll just let that sit until the data arrives. We will know that has happened when the "ready state" is "4". At that point, we can do something to actually display the data:
... function getJobs(theUrl){ document.getElementById("jobs").innerHTML = "Sending..."; var request = GXmlHttp.create(); request.open('GET', 'proxy.php?url='+URLencode(theUrl), true); request.onreadystatechange = function() { if (request.readyState == 4) { var jobDoc = GXml.parse(request.responseText); displayJobs(jobDoc); } }; request.send(null); } function displayJobs(jobDoc){ var totalResultsElement = jobDoc.documentElement.getElementsByTagName("totalresults").item(0); var numberOfJobs = totalResultsElement.firstChild.nodeValue; var jobString = "There are "+numberOfJobs+" jobs.<br />"; document.getElementById("jobs").innerHTML = jobString; } function URLencode(sStr) { return escape(sStr).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F'); } </script> ...
The first step is to turn the retreived text into XML. Once again it's Google to the rescue, with the GXml
object's static parse()
method, which keeps us from trying to figure out how to access this particular browser's parser. In fact, if no parser is available, this method uses a Javascript parser to take up the slack.
The GXml.parse()
function returns a DOM Document
, which we can then pass to the displayJobs()
function and analyze it just like any other Document
. Here we're retrieving the value of the text in the totalresults
element. (Java developers, remember the binding is slightly different for Javascript.) Once we have the results, we can display it in the jobs
div
. You can see the results here.
Actually, there is also an easier way to do this. The GXml
object also includes the value()
function, which provides the text value for an element:
... function displayJobs(jobDoc){ var totalResultsElement = jobDoc.documentElement.getElementsByTagName("totalresults").item(0); var numberOfJobs = GXml.value(totalResultsElement); var jobString = "There are "+numberOfJobs+" jobs.<br />"; document.getElementById("jobs").innerHTML = jobString; } ...
This function will come in very handy when we're ready to build the actual results:
... function displayJobs(jobDoc){ var totalResultsElement = jobDoc.documentElement.getElementsByTagName("totalresults").item(0); var numberOfJobs = GXml.value(totalResultsElement); var jobString = "There are "+numberOfJobs+" jobs.<br />"; var jobsElement = jobDoc.documentElement.getElementsByTagName("results").item(0); var jobsList = jobsElement.getElementsByTagName("result"); var numberOfJobsOnThisPage = jobsList.length; jobString = jobString + "<table>"; for (i = 0; i < numberOfJobsOnThisPage; i++){ var thisJob = jobsList.item(i); var infoUrl = GXml.value(thisJob.getElementsByTagName("url").item(0)); var jobtitle = GXml.value(thisJob.getElementsByTagName("jobtitle").item(0)); var company = GXml.value(thisJob.getElementsByTagName("company").item(0)); var city = GXml.value(thisJob.getElementsByTagName("city").item(0)); var state = GXml.value(thisJob.getElementsByTagName("state").item(0)); var country = GXml.value(thisJob.getElementsByTagName("country").item(0)); var snippet = GXml.value(thisJob.getElementsByTagName("snippet").item(0)); jobString = jobString + "<tr><td><b><a href='"+infoUrl+"'>"+jobtitle+"</a>, "+company+"</b></td>"; jobString = jobString + "<td>"+city+", "+state+", "+country+"</td></tr>"; jobString = jobString + "<tr><td colspan='2'>"+snippet+"</td></tr>"; } jobString = jobString + "<tr><td colspan ='2'>Results from <a href='http://www.indeed.com'>Indeed</a></td></tr>"; jobString = jobString + "</table>"; document.getElementById("jobs").innerHTML = jobString; }
The first step is to get a NodeList
of all of the result
elements. Once we have that, we can loop through the list, retrieving the appropriate data for each value in using it to build a string of HTML text. In this case, we're formatting the data as an HTML table. Also, as required, we're linking back to Indeed at the bottom. Once we have a string, we set it as the HTML for the jobs
div
. You can see the results here.
Okay, that's our second API. Next, we'll add a third API and put them all together.