(.*?)<\/div>/ )
title = title[0][0] if ( title[0].length >0 )
author = author[0][0] if ( author[0].length >0 )
description = ( description.length >0 ) ? description[0][0] : ''
articles.push( { :title => title,
:author => author, :description => description } )
}
}
p articles
这是基于Ruby的“屏幕截取器”。这个脚本以一个大文本字符串获取页面,然后使用一组复杂的正则表达式解析页面的标题、作者和描述元素。
在命令行运行时,将看到清单4所示的代码。
清单4. 运行fetch.rb
% ruby fetch.rb
[{:author=>"Megan Herrington", :description=>"Everything that I love about dogs I
learned in preschool", :title=>"What I like about dogs"}, {:author=>"Jack
Herrington", :description=>"How to script, produce and direct Hong Kong action
flicks", :title=>"Making action movies"}, {:author=>"Lori Herrington",
:description=>"Everything you need to know to win at Paper Mario", :title=>"Super
Paper Mario Tips"}, {:author=>"Oso Herrington", :description=>"", :title=>"Why I
bark"}]
是的,它获得了数据,但是不能获得这些元素的ID,因为HTML没有ID。
您可能想知道为什么我选择使用Ruby实现。这主要是因为它很酷,并且易于阅读,可是这里却看不到这些优点。但这不是Ruby的错。屏幕截取器本身就很复杂。它们难于编写、易于出错、难以维护,即使对接口的HTML稍加修改就可能崩溃。本文中的所有屏幕截取器代码都只有一个目的:说服您对数据设置XML接口。如果您的数据很有趣,人们就能通过某种途径找到它。如果您不支持XML,当您更新站点以“美化”界面时,就会获得屏幕截取,并且触怒客户。
添加XML服务
因此,为了避免屏幕截取问题,并制作一些炫酷的程序来使用我们的数据,我将对表格编写一个XML接口,如清单5所示。
清单5. artxml.php
require_once( "DB.php" );
$db =& DB::Connect( 'mysql://root@localhost/articles1', array() );
if (PEAR::isError($db)) { die($db-> require_once( "DB.php" );
$db =& DB::Connect( 'mysql://root@localhost/articles1', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$dom = new DomDocument();
$dom->formatOutput = true;
$root = $dom->createElement( "articles" );
$dom->appendChild( $root );
$res = $db->query( "SELECT * FROM articles" );
$rows = array();
while( $res->fetchInto($row, DB_FETCHMODE_ASSOC) ) {
$art = $dom->createElement( "article" );
$art->setAttribute( 'id', $row['id'] );
$root->appendChild( $art );
$title = $dom->createElement( "title" );
$title->appendChild( $dom->createTextNode( $row['title'] ) );
$art->appendChild( $title );
$author = $dom->createElement( "author" );
$author->appendChild( $dom->createTextNode( $row['author'] ) );
$art->appendChild( $author );
$desc = $dom->createElement( "description" );
$desc->appendChild( $dom->createTextNode( $row['description'] ) );
$art->appendChild( $desc );
}
header( "Content-type: text/xml" );
echo $dom->saveXML();
>
在命令行运行此脚本时,将获得清单6所示的输出。
清单6. 文章XML
% php artxml.php
Megan Herrington
Everything that I love about dogs I learned in
preschool
Jack Herrington
How to script, produce and direct Hong Kong action
flicks
...
这非常直观。有一个根文章标记,它包含一组文章标记。每个文章标记都有id属性(包含记录的数字id),以及存储相应数据的作者和描述标记。
我使用了PHP中的XML文档对象模型(Document Object Model,DOM)功能,而不是手动编写标记。这样DOM将为我处理所有XML节点平衡和编码工作。这是确保页面返回的XM总是有效的简便方式。强烈推荐使用XML DOM功能来输出XML。所有主要的web语言都支持构建和导出XML DOM。
获取XML
上文中我展示了从HTML中提取数据的HTML和Ruby代码。既然拥有了此XML服务,下面将观察获得相同数据的Ruby代码片断,但是这次使用XML语言。清单7显示了XML提取代码。
清单7. fetchxml.rb
require 'net/http'
require 'rexml/document'
articles = []
Net::HTTP.start('localhost', 80) { |http|
response = http.get('/ws/artxml.php')
body = response.body
doc = REXML::Document.new body
doc.each_element( '/articles/article' ) { |art|
articles.push( {
:id => art.attributes['id'],
:title => art.elements['title'].text,
:author => art.elements['author'].text,
:description => art.elements['description'].text
} )
}
}
p articles
这更简单。仍然能以相同的方式获得页面,但是将页面内存提供给REXML库,并使用XML功能轻松快捷地获得id、标题、作者和描述数据。此代码易于阅读、易于维护并且不会崩溃,除非XML格式发生变化,但这不太可能。
作为比较,我使用C#编写了相同的代码以显示如何使用两种不同的语言阅读单个数据源。如清单8所示。
清单8. WebServiceTest.cs
using System;
using System.IO;
using System.Net;
using System.Xml;
namespace wstest1
{
class WebServiceTest
{
[STAThread]
static void Main(string[] args)
{
HttpWebRequest r = (HttpWebRequest)WebRequest.Create(
"http://localhost/ws/artxml.php" );
WebResponse res = r.GetResponse();
string sPage;
StreamReader reader = new StreamReader( res.GetResponseStream() );
sPage = reader.ReadToEnd();
reader.Close();
res.Close();
XmlDocument doc = new XmlDocument();
doc.LoadXml( sPage );
foreach( XmlElement elArticle in doc.GetElementsByTagName( "article" ) )
{
string sTitle = (elArticle.SelectSingleNode( "title" )).InnerXml;
string sAuthor = (elArticle.SelectSingleNode( "author" )).InnerXml;
string sDescription = (elArticle.SelectSingleNode( "description"
)).InnerXml;
int nID = Int32.Parse( elArticle.Attributes["id"].Value );
}
}
}
}
解决了本文中最难以处理的部分后,下面应该讨论有趣的事情了,比如以其他方式使用XML可以实现什么功能。
在XSLT中使用XML
等等,我刚才是不是说最难以处理的部分已经解决了?哦,不好意思,还有另外一项。结果发现使用XML Style Sheet或XSL可以快速设置XML数据格式。清单9所示的代码设置web服务(从articles.php页面写入到HTML)所返回的XML代码的格式。
清单9. articles.xsl
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict"> xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns="http://www.w3.org/TR/xhtml1/strict">
读起来有点不太容易,但是这是您的XSL。基本上,XSL是模式匹配器,我定义了匹配传入XML树的根标记的XSL模板。它输出HTML报头,然后使用for-each循环遍历每篇文章,并输出标题、作者和描述的值(如果有)。
此样式表可以附加到XML输出本身,大多数浏览器将使用它将XML呈现到HTML以自动显示。怎么样!
Ajax
从应用程序中导出XML的最可能的理由是为了能够在web客户端中使用。客户端的JavaScript可以在加载页面之后从服务器请求XML,并以它所选择的任何方式(经常根据用户输入动态更改)呈现,并且不需要刷新页面。
清单10显示了一个基于Ajax的简单表格,它呈现来自XML feed的数据。
清单10. ajax.html
new Ajax.Request( 'artxml.php', {
method: 'get',
onSuccess: function( transport ) {
var artTags = transport.responseXML.getElementsByTagName( 'article' );
for( var a = 0; a
var author =
artTags[a].getElementsByTagName('author')[0].firstChild.nodeValue;
var title = artTags[a].getElementsByTagName('title')[0].firstChild.nodeValue;
var description =
artTags[a].getElementsByTagName('description')[0].firstChild.nodeValue;
var elTR = $('articles').insertRow( -1 );
var elTD1 = elTR.insertCell( -1 );
elTD1.innerHTML = author;
var elTD2 = elTR.insertCell( -1 );
elTD2.innerHTML = title;
var elTD3 = elTR.insertCell( -1 );
elTD3.innerHTML = description;
}
}
} );
此代码使用Prototype.js库从数据库访问数据,然后使用浏览器中的XML DOM功能访问作者、标题和描述字段。然后,使用HTML DOM函数针对数据集中的每篇文章向“articles”表格添加新行和单元格。
图3显示了Ajax代码在浏览器中的输出。
图3. 页面的Ajax版本
这是非常基础的示例,但是完全不必向服务器端请求额外的数据,即可轻松地设想添加客户端排序或搜索。
使用Flex访问XML
下一代内容丰富的Internet应用程序框架(如Adobe Flex)是基于XML产生和发展起来的。因此可以轻松使用和显示XML数据。观察清单11所示的示例Flex应用程序。
清单11. wstest.mxml
其中并没有实际代码,仅仅是对XML数据源的引用,然后此数据源被传送到DataGrid控件。图4显示了输出。
图4. 列表的Flex版本
是不是很酷?实现此功能不需要任何代码。这只是Flex 和ActionScript使用XML可以实现的基本功能。ActionScript有一个内置的语言扩展,名为E4X。借助E4X,可以像使用“点标注”语法一样简单地导航XML文档树。这意味着没有太多沉闷的XML DOM方法,仅仅是直观对象和数组引用,就像内存中有任何其他数据结构一样。
标准化
我使用了仅适于本示例的XML风格。但是也可以使用标准XML格式实现,如RSS。清单12中的代码以RSS格式显示了相同数据库输出。
清单12. artrss.php
require_once( "DB.php" );
$db =& DB::Connect( 'mysql://root@localhost/articles1', array() );
if (PEAR::isError($db)) { die($db-> require_once( "DB.php" );
$db =& DB::Connect( 'mysql://root@localhost/articles1', array() );
if (PEAR::isError($db)) { die($db->getMessage()); }
$dom = new DomDocument();
$dom->formatOutput = true;
$rss = $dom->createElement( "rss" );
$rss->setAttribute( "version", "0.91" );
$dom->appendChild( $rss );
$root = $dom->createElement( "channel" );
$rss->appendChild( $root );
$rtitle = $dom->createElement( "title" );
$rtitle->appendChild( $dom->createTextNode( "Article list" ) );
$root->appendChild( $rtitle );
$rdesc = $dom->createElement( "description" );
$rdesc->appendChild( $dom->createTextNode( "The article list" ) );
$root->appendChild( $rdesc );
$res = $db->query( "SELECT * FROM articles" );
$rows = array();
while( $res->fetchInto($row, DB_FETCHMODE_ASSOC) ) {
$art = $dom->createElement( "item" );
$root->appendChild( $art );
$title = $dom->createElement( "title" );
$title->appendChild( $dom->createTextNode( $row['title'] ) );
$art->appendChild( $title );
$title = $dom->createElement( "link" );
$title->appendChild( $dom->createTextNode(
"http://myhost/showarticle.php?id=".$row['id'] ) );
$art->appendChild( $title );
$desc = $dom->createElement( "description" );
$desc->appendChild( $dom->createTextNode( $row['description'] ) );
$art->appendChild( $desc );
}
header( "Content-type: text/xml" );
echo $dom->saveXML();
>
此方法的好处在于,除了可以阅读XML的任何自定义代码之外,还可以使用所有的RSS工具。例如,可以在feed中指向我的Firefox浏览器,就会创建可以放到工具栏并检查更新的“活动书签”,如图5所示。
图5. 使用Firefox的RSS feed
当然,不是所有数据都能方便地设置为RSS格式,这没什么问题。但是如果可以成为RSS、RDF或任何其他方便的XML格式。那么最好遵循这些格式,而不是自己发明。
结束语
希望本文能够使您以正确的角度理解应用程序的web服务。我知道并没有讲述所有REST、XML/RPC或SOAP基础知识。有很多文章讨论过这些技术,多年以来,技术人员已经历过很多基于标准的噩梦。相反,我希望展示从应用程序中获得XML数据并以实用的方式使用它是件多么轻松的事情。如果我成功了,请写信告知我并展示从您的应用程序中提取的XML数据。也许我们可以使用其他web服务一起完成一个mash-up。
资源
Flex是基于开放源码的内容丰富的Internet应用程序开发环境,由Adobe提出。
REST是简单的web服务标准,用来更直接地映射到HTTP协议。
SOAP是HTTP协议之上的高级的对象方法调用协议。
XML/RPC是HTTP协议之上的中间层方法调用协议,与SOAP相比,是略微轻量级的协议。
RSS是聚合标准,用于博客条目和新文章之类的内容。
Google的Reader服务就是web浏览器或智能电话的强大、免费的RSS管理器。
Prototype.js是免费的JavaScript库,可以帮助编写易于维护的跨浏览器Ajax代码。