30 Nov 2010 - Added link to Part 1 in Introduction and Conclusion sections. Under "Learn" in Resources, added resource item for Part 2.
Introduction
Today, almost every popular web site has a developer API, allowing web application programmers to access and manipulate data using standard frameworks such as REST and SOAP. For example, Google offers the Google Data APIs for access to Google services such as Google Buzz, Google Docs, and Google Calendar; Twitter has a REST API that allows users to search and post tweets; and Facebook offers the Graph API to manage user profiles and connections.
While these APIs certainly make your life more exciting and open the door to a variety of creative new applications, some challenges remain. For example, a key implementation challenge is the lack of uniformity between the APIs of different sites. Some sites use REST while others use SOAP; some use Atom to encode response data, but others use RSS, and still others use plain XML or JSON. As a result, every time you decide to integrate a new web service into an application, first you must do a fair amount of reading and then a fair amount of testing to fully grasp the mechanics of the service API before you begin integration.
To solve this very problem, the developers at Yahoo! decided to invent Yahoo! Query Language, better known by the acronym YQL. YQL offers a unified, SQL-like interface to multiple web service APIs, significantly simplifying the task of integrating third-party data into a web application. In this two-part article, I introduce you to YQL, illustrating how you can use it in combination with my favourite language, PHP, to build sophisticated web applications.
Understanding YQL
If you're familiar with SQL, YQL will immediately look familiar to you. Very simply, YQL treats individual web services as though they were data tables, allowing developers to formulate SQL-like queries to extract information from them. The YQL service takes care of parsing the query string, executing it on the remote service, and returning the results in standard XML or JSON format. The query string itself is passed to the YQL service using REST, as a GET request.
To better understand this approach, consider a simple example. Suppose you want to search Twitter for all posts containing the term "master chief." In a non-YQL world, you'd usually do this through the Twitter Search API, by formulating a request like this:
http://search.twitter.com/search.atom?q=master%20chief&lang=en
In return, the Twitter Search API sends back an Atom feed of results, such as the one in Figure 1.
Figure 1. An Atom feed of search results from the Twitter Search API
With YQL, you can simplify things by accessing the Twitter data table with a YQL query, as follows:
SELECT * FROM twitter.search WHERE q='master chief'
Notice that this is almost exactly like a standard SQL query: The SELECT keyword indicates this is a data retrieval operation, the FROM keyword specifies the data source, and the WHERE clause specifies the filter. After you submit this query, YQL returns a standard XML or JSON document with the results of your query from Twitter, such as the one in Figure 2.
Figure 2. The results of a YQL query on the Twitter data table
Now, skip a few days ahead and suppose that you decide to add some more data to your web application. Specifically, assume that you'd like to use the geocoding information that accompanies Twitter search results to also display a map of the geographical area from which each post originated. Also assume that you'd like to accompany the Twitter search results with a list of news headlines about the search term "master chief."
In a non-YQL world, you'd need to spend some time reading the API documentation for Yahoo! Maps and Google News before you can accomplish this task. With YQL, it's as simple as adding a couple of queries:
SELECT * FROM google.news WHERE q="master chief" SELECT * FROM maps.map WHERE latitude="XX" AND longitude="YY"
Figure 3 and Figure 4 illustrate snapshots of the query results.
Figure 3. The results of a YQL query on the Google News data table
Figure 4. The results of a YQL query on the Yahoo Maps data table
The query results in Figures 3 and 4 clearly indicate that YQL's greatest benefit lies in presenting a unified interface to third-party web services. By permitting you to query third-party services using commonly understood SQL syntax, YQL saves you time and effort and makes it easier to integrate data from disparate sources into a web application. The ability to select either XML or JSON as the output format is also useful, permitting you a degree of flexibility and allowing you to use either server-side programming (PHP Perl, for example) or client-side tools (jQuery or mooTools, for instance) to access and manipulate the result data.
Using the YQL Console
The easiest way to get started with YQL is through the YQL Console, an interactive online tool that allows you to formulate and test YQL queries on the fly. The YQL Console is hosted on the Yahoo! Developer Network and comes with diagnostics tools, example queries, and a list of available tables.
To see how it works, browse to the YQL Console and enter the following query into it, to get a list of music albums popular right now:
SELECT * FROM music.release.popular
When you submit the form, the query string is submitted to the YQL service as a URL-encoded query string. The YQL service then looks up the table definition, performs the query, and returns the results. Figure 5 illustrates what the output looks like in the YQL Console.
Figure 5. Query output in the YQL interactive console
Using YQL with PHP
As Figure 5 illustrates, YQL can return data using either XML or JSON. When building PHP web applications, XML is usually more convenient, as PHP comes with built-in XML processing extensions (SimpleXML, DOM, or XMLReader) that can be used to quickly parse the result document. With this in mind, Listing 1 attempts to perform the same query with PHP and SimpleXML:
Listing 1. Processing YQL results with SimpleXML
<?php // execute query // get list of 15 most popular music releases // retrieve result as SimpleXML object $xml = simplexml_load_file(' http://query.yahooapis.com/v1/public/yql?q= SELECT * FROM music.release.popular '); // iterate over query result set echo '<h2>Popular Music</h2>'; $results = $xml->results; foreach ($results->Release as $r) { echo '<p>'; echo '<a href="' . $r['url'] . '">' . $r['title'] . '</a> (' . $r['releaseYear'] . ') - '; echo '<a href="' . $r->Artist['url'] . '">' . $r->Artist['name'] . '</a> <br/>'; echo 'Current chart position: ' . $r->ItemInfo->ChartPosition['this'] . ' / Last chart position: ' . $r->ItemInfo->ChartPosition['last']; echo '</p>'; } ?>
Listing 1 begins by formulating a query to the YQL web service, passing it the same URL-encoded query string used in the previous example. This request is made through the simplexml_load_file()
function, thereby ensuring that the resulting XML document is automatically parsed and converted into a SimpleXML object. The remainder of the script then iterates over the <results>
node of the XML document, printing the title, link, artist, and current chart position for each album.
Figure 6 illustrates the result.
Figure 6. A list of popular music releases, retrieved through YQL
If you use the Zend Framework, you can alternatively access the YQL Web service using the Zend_Rest_Client component. Listing 2, which produces output equivalent to that of Listing 1, illustrates.
Listing 2. Processing YQL results with the Zend Framework
<?php // set up Zend auto-loader // load Zend REST client classes require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Rest_Client'); // execute YQL query // get list of most popular music releases try { $client = new Zend_Rest_Client('http://query.yahooapis.com/v1/public/yql'); $client->q('SELECT * FROM music.release.popular'); $result = $client->get(); } catch (Zend_Rest_Client_Exception $e) { echo "Client error: " . $e->getResponse(); } catch (Exception $e) { echo "Error: " . $e->getMessage(); } // iterate over query result set echo '<h2>Popular Music</h2>'; foreach ($result->Release as $r) { echo '<p>'; echo '<a href="' . $r['url'] . '">' . $r['title'] . '</a> (' . $r['releaseYear'] . ') - '; echo '<a href="' . $r->Artist['url'] . '">' . $r->Artist['name'] . '</a> <br/>'; echo 'Current chart position: ' . $r->ItemInfo->ChartPosition['this'] . ' / Last chart position: ' . $r->ItemInfo->ChartPosition['last']; echo '</p>'; } ?>
The Zend_Rest_Client component of the Zend Framework is designed specifically for developers trying to integrate PHP applications with REST-based web services. With this client, you can perform GET, POST, PUT, and DELETE responses to a REST service endpoint. REST responses are returned as instances of Zend_Rest_Client_Response objects, making it easy to access individual response properties.
Listing 2 first loads the Zend class libraries, and then initializes an instance of the Zend_Rest_Client class. This client is used to initialize an unauthenticated GET request for the YQL web service endpoint, as was done earlier in Listing 1. The returned XML file is then parsed and converted into a Zend_Rest_Client_Response object, which can then be processed using a standard foreach() loop. Notice that when using the Zend_Rest_Client object, it is not necessary to URL-encode the YQL query, as the component takes care of that step internally.
Filtering and sorting query results
As with regular SELECT queries, YQL allows you to filter query results with a WHERE clause, specify required fields, and sort results by one or more fields. To illustrate, consider the following query to the Flickr API for a list of places matching the search term "england":
SELECT * FROM flickr.places WHERE query="england"
Figure 7 illustrates the YQL response to this query.
Figure 7. A list of search results for "england", retrieved from Flickr through YQL
Listing 3 illustrates this query in practice.
Listing 3. Filtering YQL results with a WHERE clause
<?php // set up Zend auto-loader // load Zend REST client classes require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Rest_Client'); // execute YQL query // get list of Flickr places matching search term try { $client = new Zend_Rest_Client('http://query.yahooapis.com/v1/public/yql'); $client->q('SELECT * FROM flickr.places WHERE query="england"'); $result = $client->get(); } catch (Zend_Rest_Client_Exception $e) { echo "Client error: " . $e->getResponse(); } catch (Exception $e) { echo "Error: " . $e->getMessage(); } // iterate over query result set echo '<h2>Search Results</h2>'; echo '<table border=1>'; echo '<tr><th>Name</th><th>Latitude</th><th> Longitude</th></tr>'; foreach ($result->place as $p) { echo '<tr>'; echo '<td>' . $p . '</td>'; echo '<td>' . $p['latitude'] . '</td>'; echo '<td>' . $p['longitude'] . '</td>'; echo '</tr>'; } echo '</table>'; ?>
Figure 8 displays the formatted output of Listing 3.
Figure 8. A formatted list of search results for "england", retrieved from Flickr through YQL
You can add additional filter criteria with AND and OR operators. Consider this revision of the previous query, which further filters results by timezone
:
SELECT * FROM flickr.places WHERE query="england" AND timezone LIKE "%europe%"
You can restrict the output of the query to specific fields only. This approach is useful when you need only a small subset of the data returned by the web service, and it also reduces the size of the response packet. Here's a revision of the previous example, which retrieves only the latitude, longitude, and name of each record:
SELECT latitude, longitude, content FROM flickr.places WHERE query="england"
YQL also supports various utility functions that can be used to sort, count, and eliminate duplicates from the result set. These functions are usually placed after the YQL query string, separated with a pipe (|) character. Consider the following query, which sorts results by latitude:
SELECT latitude, longitude, content FROM flickr.places WHERE query="england" | sort (field="latitude")
You can also apply the unique filter to strip duplicates from the result, specifying the field that should be checked:
SELECT * FROM flickr.places WHERE query="england" | unique (field="timezone")
Listing 4 puts all of this together to build an interactive query tool that asks the user to input a location name and returns a sortable list of results.
Listing 4. Searching for place names
<html> <head></head> <body> <form method="post" action="<?php echo htmlentities ($_SERVER['PHP_SELF']); ?>"> Search term: <input type="text" name="q" /> Sort results by: <select name="s"> <option value="timezone">Time zone</option> <option value="latitude">Latitude</option> <option value="longitude">Longitude</option> </select> <input type="submit" name="submit" value="Search" /> </form> <?php // check if form is submitted // perform necessary validation (omitted for brevity) if (isset($_POST['submit'])) { // set up Zend auto-loader // load Zend REST client classes require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Rest_Client'); // execute YQL query // get list of Flickr places matching search term // sort by requested field try { $client = new Zend_Rest_Client('http://query.yahooapis.com/v1/public/yql'); $client->q( 'SELECT latitude, longitude, timezone, content FROM flickr.places WHERE query="' . $_POST['q'] .'" | sort(field="' . $_POST['s'] . '")'); $result = $client->get(); } catch (Zend_Rest_Client_Exception $e) { echo "Client error: " . $e->getResponse(); } catch (Exception $e) { echo "Error: " . $e->getMessage(); } // iterate over query result set echo '<h2>Search Results</h2>'; echo '<table border=1>'; echo '<tr><th>Name</th><th>Timezone</th> <th>Latitude</th><th>Longitude</th></tr>'; foreach ($result->place as $p) { echo '<tr>'; echo '<td>' . $p . '</td>'; echo '<td>' . $p['timezone'] . '</td>'; echo '<td>' . $p['latitude'] . '</td>'; echo '<td>' . $p['longitude'] . '</td>'; echo '</tr>'; } echo '</table>'; } ?> </body> </html>
Figure 9 illustrates Listing 4 in action.
Figure 9. An interactive search and sort filter for place names
For a complete list of YQL functions, look in the YQL Guide (see Resources for a link).
An example application: Weather forecasts by location
Let's now take all that you've learned so far and build a simple YQL-backed application using PHP. Listing 5 asks the user to enter his or her location into a form; it then connects to the Yahoo! Weather service through the weather.bylocation table and queries for a local weather forecast for that location. Take a look at the code in Listing 5.
Listing 5. Retrieving weather forecasts by location
<html> <head></head> <body> <form method="post" action="<?php echo htmlentities ($_SERVER['PHP_SELF']); ?>"> Enter city name: <input type="text" name="city" /> <input type="submit" name="submit" value="Get forecast" /> </form> <?php // check if form is submitted // perform necessary validation (omitted for brevity) if (isset($_POST['submit'])) { // set up Zend auto-loader // load Zend REST client classes require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Rest_Client'); // execute YQL query // get Yahoo! Weather forecast for selected zip code try { $client = new Zend_Rest_Client('http://query.yahooapis.com/v1/public/yql'); $client->env('store://datatables.org/alltableswithkeys'); $client->q('SELECT * FROM weather.bylocation WHERE location="' . $_POST['city'] . '"'); $result = $client->get(); } catch (Zend_Rest_Client_Exception $e) { echo "Client error: " . $e->getResponse(); } catch (Exception $e) { echo "Error: " . $e->getMessage(); } // process and print query results $data = $result->results->weather->rss->channel->item; echo '<h2>' . $data->title . '</h2>'; echo $data->pubDate . '<br/>'; echo $data->description; } ?> </body> </html>
After the web form is submitted with a city name, Listing 5 uses the Zend_Rest_Client component to send a YQL query to the weather service. The query uses a WHERE clause to filter the contents of the weather.bylocation table and retrieve a weather forecast for the specified zip code. The query result is formatted and presented as an HTML page. Figure 10 illustrates the result.
Figure 10. Weather forecasts by city, retrieved through YQL
Notice also that Listing 5 adds a new variable to the query string, env
. This variable is necessary because the weather.bylocation data table is a so-called community table, maintained by the community and not by Yahoo! itself. As a result, the YQL service does not automatically know where this table definition is located. The env
variable is used to specify the location of the table definition files. In this case, the location is the Community Open Data Tables for YQL web site, which serves as a repository for all YQL community tables (see Resources for a link).
Using nested YQL queries
The greatest value of a traditional RDBMS comes from its ability to join individual tables together to create different views of the data contained therein. And one of the coolest things about YQL is that it lets you do the same thing with web services. Using YQL queries, it is possible to combine data from multiple web services to present new and useful views of third-party data.
While the possible applications of this feature are limited only by your creativity, a simple example can help make clear the power of this feature. Consider Listing 6, which asks the user to enter a country name and then queries the upcoming web service to list upcoming events in that country.
Listing 6. Searching for events by country
<html> <head></head> <body> <form method="post" action="<?php echo htmlentities($_SERVER['PHP_SELF']); ?>"> Search for events in: <input type="text" name="country" /> <input type="submit" name="submit" value="Search" /> </form> <?php // check if form is submitted // perform necessary validation (omitted for brevity) if (isset($_POST['submit'])) { // set up Zend auto-loader // load Zend REST client classes require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Rest_Client'); // execute YQL query // get list of events in requested country try { $client = new Zend_Rest_Client('http://query.yahooapis.com/v1/public/yql'); $client->q( "SELECT * FROM upcoming.events WHERE country_id IN (SELECT id FROM upcoming.country WHERE name = '" . ucfirst($_POST['country']) . "')"); $result = $client->get(); } catch (Zend_Rest_Client_Exception $e) { echo "Client error: " . $e->getResponse(); } catch (Exception $e) { echo "Error: " . $e->getMessage(); } // iterate over query result set echo '<h2>Events in ' . ucfirst($_POST['country']) . '</h2>'; foreach ($result->event as $e) { echo '<p>'; echo '<a href="' . $e['url'] . '">' . $e['name'] . '</a> <br/>'; echo 'Starts: ' . date('d M Y', strtotime($e['start_date'])). '<br/>'; echo !empty($e['end_date']) ? 'Ends: ' . date('d M Y', strtotime($e['end_date'])) . '<br/>' : ''; echo 'Location: ' . $e['venue_name'] . '<br/>'; echo 'Address: ' . sprintf('%s, %s', $e['venue_address'], $e['venue_city']) . '<br/>'; echo $e['description'] . '<br/>'; echo '</p>'; } } ?> </body> </html>
Listing 6 makes use of two nested YQL queries. The inner query accesses the "upcoming.country" data table to retrieve the numeric ID corresponding to the country string entered by the user. This numeric ID is then passed to the outer query, to retrieve a list of events linked to that country ID.
Figure 11 illustrates an example of the result.
Figure 11. A list of upcoming events, searchable by country
It's important to note that technically, this is not a join, but a subquery. As of this writing, YQL doesn't allow you to link tables through common keys (a join), but only permits you to use the results of one query inside another (a subquery).
An example application: Bestseller lists and prices
As you might imagine, the ability to combine data from multiple web interfaces using simple SQL-like syntax is music to the ears of mashup developers. And so, let's consider another, slightly more complicated application: combining data from The New York Times bestseller lists with price information from Amazon.com's database to present a composite view of popular books, their prices, and their popularity.
The first step in building this application is to retrieve the current list of New York Times bestsellers. YQL makes this information available through its nyt.bestsellers table, but you need a valid API key to run queries on this table. Assuming you have this key (look in Resources for a link that explains how to get it), you can retrieve the current list of hardcover fiction bestsellers for the week of July 21, 2010 using a query like this:
SELECT * FROM nyt.bestsellers WHERE listname='Hardcover Fiction' AND date='2010-07-21' AND apikey='NYT-API-KEY'
Figure 12 illustrates what the output of this query looks like.
Figure 12. A list of New York Times bestsellers, retrieved through YQL
Notice that each record in the result lists the book's unique ISBN number. This is critical information for the second part of the application, which needs to look up the price of the book on Amazon.com.
Pulling pricing data from Amazon.com might seem like an onerous task at first glance, but, in reality, it couldn't be simpler. Amazon.com exposes its product database to third-party developers through its Amazon ECS web service (see Resources for a link and information on how to obtain an API key). And YQL includes a data table for Amazon ECS, making it possible to retrieve pricing information for a particular ISBN with a query like this one:
SELECT DetailPageURL, ItemAttributes, SalesRank, MediumImage FROM amazon.ecs WHERE AWSAccessKeyId='AWS-KEY' AND secret='AWS-SECRET-KEY' AND ResponseGroup='Medium' AND Operation = 'ItemLookup' AND ItemAttributes.Binding = 'Hardcover' AND ItemId = '1400065453'
Figure 13 illustrates the output of this query.
Figure 13. Product data from Amazon.com, retrieved through YQL
It should be clear that the two preceding queries can be easily combined to produce the necessary information. Listing 7 has the complete script. Remember to replace the dummy API keys in the queries with your own before trying it out.
Listing 7. Retrieving bestseller lists and prices
<html> <head> <style type="text/css"> .item { float: left; width: 400px; padding:10px; } .cover { float:left; padding: 5px; border: solid 1px black; } .data { margin-left: 150px; font-weight: bolder; } </style> </head> <body> <?php // set up Zend auto-loader // load Zend REST client classes require_once 'Zend/Loader.php'; Zend_Loader::loadClass('Zend_Rest_Client'); // execute YQL query // get list of NYT bestsellers // retrieve image and price from Amazon.com try { $client = new Zend_Rest_Client('http://query.yahooapis.com/v1/public/yql'); $client->env('store://datatables.org/alltableswithkeys'); $client->q( "SELECT DetailPageURL, ItemAttributes, SalesRank, MediumImage FROM amazon.ecs WHERE AWSAccessKeyId='AWS-KEY' AND secret='AWS-SECRET-KEY' AND ResponseGroup='Medium' AND Operation = 'ItemLookup' AND ItemAttributes.Binding = 'Hardcover' AND ItemId IN (SELECT isbns.isbn.isbn10 FROM nyt.bestsellers WHERE apikey='NYT-KEY' AND listname='Hardcover Fiction' AND date='2010-07-20') | unique(field='ItemAttributes.Title')"); $result = $client->get(); } catch (Zend_Rest_Client_Exception $e) { echo "Client error: " . $e->getResponse(); } catch (Exception $e) { echo "Error: " . $e->getMessage(); } // iterate over query result set echo '<h2>New York Times - Hardcover Fiction Bestsellers</h2>'; $count = 1; foreach ($result->results->Item as $r) { echo '<div class="item">'; echo '<img class="cover" src="' . $r->MediumImage->URL . '"/>'; echo '<div class="data">'; echo $count . '. '; echo '<a href="' . $r->DetailPageURL . '">' . $r->ItemAttributes->Title . '</a>'; echo ' - '. $r->ItemAttributes->Author . '<br/>'; echo 'Amazon.com Sales Rank: ' . $r->SalesRank . '<br/>'; echo 'Amazon.com Price: ' . $r->ItemAttributes->ListPrice->FormattedPrice . ''; echo '</div></div>'; $count++; } ?> </body> </html>
Listing 7 combines the two preceding queries, using the ISBN key as the common denominator, to produce a composite result containing book titles, authors, images, prices, and sales ranks. This result is then parsed and processed to generate an HTML page (Figure 14).
Figure 14. A list of bestselling books, combined with product data through YQL
Notice that in Listing 7, the inner query specifically retrieves only the ISBN-10 numbers of the books on the bestseller lists. Because this information is a few nodes down in the tree, dot notation is used to indicate the exact hierarchical position of the required nodes to the query parser. A similar technique is used when applying the unique filter to the outer query, with dot notation used to indicate the field by which results are to be filtered.
Conclusion
As these examples illustrate, YQL is a powerful tool for web application developers: It presents a unified interface to different web services, enabling a standard SQL-like query mechanism that speeds up development and requires less information about the target service. You can filter YQL results with WHERE clauses, and combine or "mash up" data from multiple services through the use of sub-selects. Add PHP, with its powerful XML processing tools, to the equation, and you've got a combination that even the most jaded web developer can enjoy experimenting with!
The examples in this article have merely touched the tip of the iceberg. You can do a lot more with YQL, including paging result sets; extracting data from RSS, Atom, XML, and HTML documents; and adding and modifying data with CREATE and UPDATE queries. I'll cover more in the second part of this article, so make sure you come back soon.
Resources
Learn
- Building web applications with YQL and PHP, Part 2: Use PHP and YQL to retrieve and combine data from multiple web services (Vikram Vaswani, developerWorks, November 2010): Dig deeper into the Yahoo! Query Language (YQL). Use YQL in a PHP application to add, edit, and delete data on third-party web services. Also search through and retrieve data from HTML pages plus structured XML formats such as RSS and Atom.
- The YQL Guide: Learn more about YQL and access Internet data with SQL-like commands.
- The YQL Console: Experiment with YQL queries.
- YDN Forums > YQL: Participate in discussions related to YQL development.
- The Zend_Rest_Client library: Read more about the Zend_Rest_Client library in a searchable Reference Guide.
- Community YQL tables: Review a complete list of Open Data Tables that describe how to map YQL onto any web service or source on the Internet.
- The Twitter Search API: Learn more in the official reference for the API that exposes Twitter data.
- The New York Times API: Learn more about this source of news, information, and data. Request an API key and get started.
- Amazon.com's ECS Web services: Find all the information you need to create Web sites or apps that integrate ECS plus diagnose and resolve any problems. Request an API key and start coding.
- More articles by this author (Vikram Vaswani, developerWorks, August 2007-current): Read articles about XML, additional Google APIs and other technologies.
- XML area on developerWorks: Get the resources you need to advance your skills in the XML arena.
- My developerWorks: Personalize your developerWorks experience.
- IBM XML certification: Find out how you can become an IBM-Certified Developer in XML and related technologies.
- XML technical library: See the developerWorks XML Zone for a wide range of technical articles and tips, tutorials, standards, and IBM Redbooks. Also, read more XML tips.
- developerWorks technical events and webcasts: Stay current with technology in these sessions.
- developerWorks on Twitter: Join today to follow developerWorks tweets.
- developerWorks podcasts: Listen to interesting interviews and discussions for software developers.
- developerWorks on-demand demos: Watch demos ranging from product installation and setup for beginners to advanced functionality for experienced developers.
Get products and technologies
- The Zend Framework: Download the latest Zend Framework releases.
- IBM product evaluation versions: Download or explore the online trials in the IBM SOA Sandbox and get your hands on application development tools and middleware products from DB2®, Lotus®, Rational®, Tivoli®, and WebSphere®.
Discuss
- XML zone discussion forums: Participate in any of several XML-related discussions.
- developerWorks blogs: Check out these blogs and get involved.
Comments
Dig deeper into XML on developerWorks
- Overview
- New to XML
- Technical library (tutorials and more)
- Forums
- Downloads and products
- Open source projects
- Standards
- Events
-
Bluemix Developers Community
Get samples, articles, product docs, and community resources to help build, deploy, and manage your cloud apps.
-
developerWorks Weekly Newsletter
Keep up with the best and latest technical info to help you tackle your development challenges.
-
DevOps Services
Software development in the cloud. Register today to create a project.
-
IBM evaluation software
Evaluate IBM software and solutions, and transform challenges into opportunities.