xml,Atom是什么

Posted on 2007-12-07 21:30 yukui 阅读(4008) 评论(0)  编辑  收藏 所属分类: 技术

Atom 是一种格式还是一种协议?两者都是!将其用于联合和发布

 

 



级别: 中级

Dethe Elza (delza@livingcode.org), 技术架构师, Justsystems
David Mertz (mertz@gnosis.cx), 作者, Gnosis Software, Inc.

2006 年 10 月 27 日

Atom 实际上是两种不同的、都与联合(blog、新闻提要和其他定期更新的信息)有关的东西。Atom Syndication Format 是用于发布条目(单个主题或者项)和提要(主题或项的集合)的 IETF 标准。Atom Publication Protocol(有时候称为 Atom API 或缩写为 APP)是从 Atom 资料库中发现、列表、添加、编辑和删除内容的方法。尽管作为联合格式的 Atom 已经通过 IETF 审查成为一项标准,但标准委员会仍然在研究作为发布协议的 Atom,虽然目前来看大部分似乎已经确定。

开始学习 Atom 激动人心的所有方面。

Atom 联合

作为一种联合格式,Atom 来源于各种 RSS(有很多)的实践而不是完全从头创建的,只不过明确了 RSS 规范中含糊的地方使其更有用(比如 RSS 没有规定 title 元素能否包含标记),改正有问题的一些地方(比如确定不同提要中的重复项,这种情况在聚合内容中经常出现)。原来用于 RSS 提要的多数客户机和工具现在都支持或准备支持 Atom 内容,既然它解决了 RSS 中存在的问题,我推荐使用这种格式。但基本上来说 Atom 是对 RSS 的逐步改进而不是革命性的改造。Atom 采取的一个演化步骤是支持两种基本类型的联合文档:提要和项。提要与 RSS 类似,都是项的集合。但项也可以是独立的文档,本身包含一张帖子(可能是一条新闻或者 blog 贴子)或者对外部文档的引用,比如图片。优于两者兼备,所以这种联合格式为建立发布协议提供一个灵活的层面。

清单 1 显示了 ATom 提要的一个小例子:


清单 1. Atom 提要
<?xml version='1.0' encoding='UTF-8'?>
                        <feed xmlns='http://www.w3.org/2005/Atom'>
                        <id>urn:uuid:E14A6C6B-A832-4AE9-9D67-263181407D5E</id>
                        <link rel="self" href="/temp/index.atom"/>
                        <updated>2006-04-19T18:43:55Z</updated>
                        <title type='text'>Cool gizmos</title>
                        <subtitle type='text'>The latest in high-tech gizmos.</subtitle>
                        <author>
                        <name>Dethe Elza</name>
                        <email>dethe.elza@livingcode.org</email>
                        </author>
                        <entry>
                        <id>urn:uuid:2C31F522-20A6-44DF-AA63-6524441FF6A3</id>
                        <published>2006-05-02T19:00:10Z</published>
                        <updated>2006-04-30T20:57:20Z</updated>
                        <title type='text'>This new gizmo is hot, hot hot!</title>
                        <content type='text'>I just got my hands on the latest gizmo
                        that's sweeping the nation: it's totally rad.</content>
                        </entry>
                        </feed>
                        





回页首


协议、更多的协议和 API

作为发布协议,Atom 具有更大的抱负。曾经尝试过几次创建管理 Web 内容(比如 blog)的通用协议(尽管常常被误称为 API),但要么缺少必要的功能,要么需要工作区或者私有接口。最重要的是,这些尝试都没有利用好的 Web 应用实践,如 REST。LiveJournal 是第一次尝试,但是它把一切都通过 HTTP POST 传输而忽略了 GET、PUT 和 DELETE,也没有使用 HTTP 身份验证。XML-RPC 也走了同样的路线,与 LiveJournal 协议一样通过 URI 发送所有信息。直到最近,XML-RPC 还将字符串限制为 ASCII,因而首先就放弃了 XML 的主要优点之一。虽然 XML-RPC 本身不是一个发布协议,但一些协议是以它为基础的,包括 Manila RPC、Blogger API、MetaWeblog API 以及 LiveJournal XML-RPC Client/Server 协议。这些协议都不支持国际化,都使用明文传递口令,都不容易扩展。Atom Publishing Protocol 充分利用了 XML(国际化,可使用 XML 名称空间扩展)和 HTTP(所有的方法、身份验证、用 URI 标识资源)。

Atom Publishing Protocol 以 weblog 协议为基础但又超越了 weblog 协议,是一种管理 Web 内容的方便工具,得到很多应用,其中包括 Bugzilla、Google Data APIs Protocol 以及 上一期 “XML 问题” 文章(请参阅 参考资料)中提到的很多日程安排站点,还有一个当时没有提到但下面就要讨论的重要站点。

我认为 Atom Publishing Protocol 是通往可写入 Web 的征途中的一个重要里程碑。从来 Web 都是双向的,PUT 和 DELETE 方法一直是 HTTP 的一部分,但在读/写 Web 的前进途中出了岔子。我们已经通过 wiki、XML-RPC 和大块头 WebDAV 缓慢地转回原来的 Web 之路。WebDAV 或简称 DAV,即分布式编辑和版本协议(Distributed Authoring and Versioning),其目标是 “完成 Web 的最初目标,成为一种可写的、协作媒介”(引自 WebDAV FAQ)。因此将 APP 与 DAV 进行对比是公平的。DAV 提供的功能远远超过 APP,包括阻塞、任意元数据的存储以及存储资源的删除或重命名。公平地说,Atom 也能支持任意元数据,因为很容易通过 XML 名称空间扩展。Atom 实际上对协作的支持比较弱,只有发布(包括以后的编辑),因此对阻塞的需要不大,而且可以用 DELETE 后跟 PUT 来对资源重命名。Atom 完成这些功能不需要扩展 HTTP 或者增加新的方法。WebDAV 极其复杂,一直受到主供应商糟糕的、漏洞百出的实现的困扰。即便如此,WebDAV 社区仍然不满足 DAV 对 HTTP 的扩展,因此又层层加码(或者准备)作进一步的扩展,比如 Advanced Collections、Versioning and Configuration Management(因为您知道,Distributed Authoring and Versioning 不支持版本化)和 Access Control。但是这些扩展对日程安排来说还不够,因此出现了 CalDAV,它对 HTTP 做了更多扩展。

可能毫不奇怪,WebDAV 一直未能成功地征服 Web。难以实现,而且设置和管理都很麻烦。DAV 也有自己的成功支持,特别是 Subversion,这个版本控制系统被夸耀成 CVS 的后继者。Subversion 建立在 DAV 和 Versioning 扩展(也称为 DeltaV)的基础上,虽然它仅仅选择 DAV 中相关的部分而丢掉了其他功能,然后在这个混合物中加上自己的协议。虽然 Subversion 很成功,但是它实际上没有证明 DAV 巴洛克式的复杂性的正确性。对于 DAV 功能中的 90%,我认为 APP 更合适,剩下的 10% 可放到其他系统中。APP 本身并不是一个包罗万象的终极解决方案。它本身没有解决身份验证的问题,没有提供查询机制,当然也没有支持实时协作这类功能的打算。我认为读/写式 Web 仍在成长之中,我期望也许有一天 Jabber XMPP 协议会嵌入到 Web 浏览器中作为双向实时端对端协议,但这是后话了。

James Tauber 正在从事一个与 Atom 和 Subversion 有关的 Python 项目,称为 Demokritos,该项目提供了 Atom Store。有趣的地方(至少对于本文来说)在于底层使用 Subversion 来提供持久性。项目仍然处在早期阶段(今后的版本将增加身份验证),但是值得关注其进展。Google Base 可以看作是 Atom Store 的商业版本(虽然多数上传使用现在已经过时的 Atom Syndication Format 0.3 版),Amazon 的 S3 数据存储从使用 HTTP GET/PUT/DELETE 这一点看在概念上类似于 Atom Publishing Protocol。有选择当然好,但是如果所有的选择都统一到一个简单、健壮的标准上就更好了。

Atom Publishing Protocol 的工作原理是,客户机可以查询自省(introspection)文档,这类文档列出了提供的内容集合、能力(比如可读和可写)及其地址 URI。然后客户机可以查询集合本身来发现与其自身包含的内容类似的信息,可以是 Atom Entries 或图片、音频、视频之类的媒体。集合是 Atom 提要,增加新的材料只需要 PUT 一个 Atom Entry 文档并收到指向该资源的 URI,然后可以对该资源作进一步处理(用 POST 编辑,用 GET 读,用 DELETE 删除)。比如,清单 2 是一个简单的自省文档:


清单 2. 自省文档的例子
<?xml version="1.0" encoding='utf-8'?>
                        <service xmlns="http://purl.org/atom/app#">
                        <workspace title="Gizmo Page" >
                        <collection
                        title="Cool gizmos"
                        href="http://example.org/gizmo/index.atom" >
                        <member-type>entry</member-type>
                        </collection>
                        <collection
                        title="Photos of Gizmos"
                        href="http://example.org/gizmo/image" >
                        <member-type>media</member-type>
                        </collection>
                        </workspace>
                        </service>
                        

这个自举过程中仍然有一个问题:一开始如何发现自省文档?有一个虽然过期但是广泛实现的 IETF 草案 Atom Feed Autodiscovery,它描述了如何在 HTML 页面的元数据中嵌入一个或多个 Atom 提要引用。该方法对自省文档也同样有效。这项技术很简单,在 HTML 文档的 <head> 中插入一个 <link> 元素,其 rel 属性包含关键字 “alternate”,type 属性值为 “application/atom+xml”,href 属性指向一个 Atom 提要。虽然 Atom 工作组没有明确说明这种方法如何用于自省文档,但大致与上述形式类似,只不过如 Atom wiki 上所述作以下变更:rel 属性为 “introspection”,type 属性必须是 “application/atomserv+xml”,href 则指向一个自省文档而不是提要。无论哪种情况,<link> 元素都应该在 title 属性中包含人类可读的值。比如:

<link rel="instrospection" type="application/atomserv+xml"
                        href="/introspection.atomsrv" title="All about my feeds"/>
                        





回页首


为什么 Atom 必须支持微格式?

上一期 “XML 问题” 文章中,我提到只有与 Atom 结合起来微格式才能真正得到应用(请参阅 参考资料)。这句话现在仍然有效。Uche Ogbuji 是一位 Atom 拥趸,但是对微格式的使用持不同意见。专门针对 Atom 的微格式目前有两种(据我所知):hAtom 是 Atom 的一个子集,此外还有关于 “XHTML Microformats for the Atom Publishing Protocol” 的一个 IETF 草案,它提出了两种微格式用于在 APP 中描述类别和错误。我还没有发现这些微格式的应用。

最有趣的是,微格式是用于嵌入其他文档的,而 Atom 文档的目的是包含 HTML 或 XHTML 片段(以小心控制的方式)。因此没有理由 Atom 提要中不能嵌入 hCalendar 制定的日历。事实上,就在上一篇文章进入最终编辑阶段时,Google 发布了它的 Calendar 产品,该产品允许以 Atom 格式订阅日历提要。不幸的是,虽然能够以包含 iCalendar 信息的方式获得提要,但 Google 没有在提要中提供 hCalendar。一些人注意到了这一点,编写了 GreaseMonkey 脚本之类的东西在网页中发现 hCalendar 并将其增加到 Google Calendar 中,但我希望选择另一条路,使用我的 Google Calendar Atom 提要向 hCalendar 格式的 weblog 增加事件。快速搜索后没有发现别人做过这个,我只好自己写了一个简单、粗糙的脚本。我是用 Python 编写的,它需要两个第三方库:httplib2 和 ElementTree,链接参见 参考资料。这仅仅是个例子,与一般的例子一样没有包含错误检查,也不是很灵活。清单 3 显示了示范其用法的测试函数:


清单 3. Atom 到 weblog 的脚本
'''
                        Utility to grab a Google Calendar feed and return hCalendar code
                        This module has one public function:
                        events_for_feed(feed_uri, start, end) -> [hCalendarEvents]
                        '''
                        import StringIO
                        import httplib2
                        import cElementTree
                        _ATOM_NS = 'http://www.w3.org/2005/Atom'
                        _GDATA_NS = 'http://schemas.google.com/g/2005'
                        _MONTHS = [None, 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
                        'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
                        _EVENT_TEMPLATE = '''<div class="vevent"
                        xmlns="http://www.w3.org/1999/xhtml">
                        <abbr class="dtstart" title="%(start)s">%(start_hr)s</abbr> -
                        <abbr class="dtend" title="%(end)s">%(end_hr)s</abbr> -
                        <span class="summary">%(summary)s</span> - at
                        <span class="location">%(where)s</span>
                        <div class="description">%(description)s</div>
                        </div>
                        '''
                        def _end_human_readable(ts):
                        return ts[11:16]
                        def _start_human_readable(ts):
                        month = _MONTHS[int(ts[5:7], 10)]
                        return '%s %s, %s - %s' % (month, ts[8:10], ts[:4], ts[11:16])
                        def _entries_for_feed(feed_uri, start, end):
                        h = httplib2.Http('.cache')
                        resp, content = h.request('%s?start-min=%s&start-max=%s' %
                        (feed_uri, start, end), 'GET')
                        doc = cElementTree.parse(StringIO.StringIO(content))
                        return doc.findall('//{%s}entry' % _ATOM_NS)
                        def _event_for_entry(entry):
                        when = entry.find('{%s}when' % _GDATA_NS)
                        start = when.get('startTime')
                        start_hr = _start_human_readable(start)
                        end = when.get('endTime')
                        end_hr = _end_human_readable(end)
                        where = entry.find('{%s}where' % _GDATA_NS).get('valueString')
                        summary = entry.findtext('{%s}title' % _ATOM_NS)
                        description = entry.findtext('{%s}content' % _ATOM_NS)
                        return locals().copy()
                        def events_for_feed(feed_uri, start, end):
                        return [_EVENT_TEMPLATE % _event_for_entry(entry) for entry in
                        _entries_for_feed(feed_uri, start, end)]
                        def test():
                        feed_uri = 'http://www.google.com/calendar/feeds/\
                        dethe.elza@gmail.com/public/full'
                        start = '2006-04-30T00:00:00'
                        end = '2006-05-30T00:00:00'
                        for event in events_for_feed(feed_uri, start, end):
                        print event
                        if __name__ == '__main__':
                        test()
                        

将该脚本增加到 Calendar 提要中并提供起始日期和结束日期,就会返回一列 hCalendar 格式的字符串,可以插入到 weblog 中。这就是读/写式 Web 的优美之处。如果某一方不支持您的格式,但是他们使用的格式是开放的并提供相应文档,就像 google 那样,您可以根据需要自行创建。我的观点是,微格式和 Atom 是互不可分的,这一观点虽然还没有得到证实,但至少以不同的方式说明了两者可以协同工作(仍然有很多工作要做)。





回页首


结束语

关于 Atom Publication Protocol 的研究仍在继续,其他相关规范如 Google Calendar 扩展同样如此。网站在快速地采用 Atom,应用程序和编程工具也在适应 Atom。开放的格式、可扩展性和清晰的定义,使得 Atom 对于 Web 影响力有可能像关系数据库对企业一样。HTTP GET 和 View Source 时至今日仍然是一种有效的组合,就像在 Web 早期一样。

通过这篇简短的介绍,我希望您能了解 Atom Syndication 格式的重要性以及 Atom Publication Protocol 如何简化它的使用。要进一步了解如何使用这些新技术,请参阅 参考资料



参考资料

学习

获得产品和技术
  • Universal Feed Parser:该 Python 程序能够解析所有已知的 RSS 和 Atom 格式,主要目标是最大可能地提取数据而不是保证格式的有效性,就是说能够解析很多严格来说不正确的提要。

  • Joe Gregorio 的 Python 库 Httplib2(客户端 HTTP):支持 HTTP 1.1、HTTPS、三种 HTTP Authentication 形式、缓冲、压缩、所有 HTTP 方法等。

  • Subversion:这种版本控制工具使用 WebDAV 为 CVS 提供了递增式改进。

  • Demokritos:James Tauber 的 Atom Store 用 Python 编写,使用 Subversion 持久。

  • Feed Validator:检查(您或者别人的)Atom 提要中的错误。

  • Atom Publishing Protocol Test Suite:检查支持 APP 的站点是否遵循该协议。(包括 HTTP 身份验证。)

  • ElementTree:Frederik Lundh 开发的 Python XML 库。


作者简介

Dethe Elza 的照片

Dethe Elza 最喜欢的职位是首席疯狂科学家(Chief Mad Scientist)。可以通过 delza@livingcode.org 与 Dethe 联系。他有自己的博客 http://livingcode.org/,主要是关于 Python 和 Mac OS X 的,他为自己的孩子编写程序。欢迎对本专栏提供建议和意见。


David Mertz 的照片

David Mertz 是一位开放标准的虔诚信奉者,仅对这类标准的冗长略有微词。可以通过 mertz@gnosis.cx 与 David 联系,在 http://gnosis.cx/dW/ 上有关于他的生活的详细介绍。欢迎对本文、以前及将来的专栏文章提出建议和意见。请阅读 David 的著作 Text Processing in Python


只有注册用户登录后才能发表评论。


网站导航:
 

posts - 131, comments - 12, trackbacks - 0, articles - 32

Copyright © yukui