今天的Web主要是面向人类的使用而建立的。即使Web上已经开始出现机器可读的数据,它们通常仅仅是将数据和格式分存在独立的文件中,并且在面向人类和面向机器的版本中保留非常有限的对应。结果,Web浏览器在解析和处理Web数据时仅能向人类提供最小的辅助:浏览器仅仅理解关于如何去显示的信息。我们引入RDFa来提供一个XHTML的属性集合,用来为可视化数据增加机器可读的信息。我们将介绍如何使用RDFa来表达简单的和复杂的数据集,特别是如何在不重复内容的同时将现有的人类可见的文本与链接转换为机器可读的数据。
本文档仅作为RDFa入门。RDFa的正式规范可以在[RDFA-SYNTAX]中找到。
本节描述本文档在其发表时的状态。其它文档可以取代本文档。当前W3C出版物的列表和本技术报告的最新版本可以参见位于http://www.w3.org/TR/的W3C技术报告索引。
本文档是一份由W3C语义Web部署工作组 [SWD-WG]和W3C XHTML2工作组 [XHTML2-WG]联合创作的工作组记录。这项工作是W3C语义Web行动和HTML行动的一部分。在本文档转换为工作组记录的同时,RDFa语法规范转换为了W3C推荐标准。
RDFa入门的这个版本包括了对前一个版本的少许编辑上的修改,并增加了简短的一节(4.1)为那些希望创建新的关系词汇表的人们提供指南。所有的修改在差异文档中详细叙述。工作组已经收到了扩展本文档的建议,并且工作组可能在未来增加内容,但并不承诺会这么做。
欢迎将对这份工作组记录的意见建议发送到public-rdf-in-xhtml-tf@w3.org;请在邮件主题中加入"comment"字样。这个邮件地址收到的所有邮件在公共档案中可以查看。
作为工作组记录发表并不意味着被W3C全体成员认可。这是一个草案并可能在任何时刻被更新、替换或废止。本文档被引用的唯一合适方式是作为正进行中的工作。
本文档是由根据2004年2月5日发布的W3C专利政策来运作的团队制定的。W3C维护着一个与XHTML 2工作组的交付产品有关的已公开的专利列表,以及另一个与语义Web部署工作组的交付产品有关的已公开的专利列表;这些页面中也包括如何公开一项专利的说明。一个对专利拥有实际知识并相信该专利包含了基本要求的个人必须根据W3C专利政策第6节的要求公开这个信息。
1 引言
1.1 HTML vs. XHTML
2 给XHTML加点调料
2.1 为你的作品指定许可协议
2.2 对题目和作者加标签
2.3 一页多项
3 更进一步
3.1 联系信息
3.2 社会网络
4 关于RDF
4.1 自定义词汇表
5 了解更多
6 致谢
7 参考文献
Web是一个丰富的分布式的互联信息仓库,这些信息主要是面向人类的使用组织起来的。在一张典型的网页上,一个XHTML作者可能指定了一个标题,接下来用小字号写了一个子标题、一块斜体的文本、几段中等字号的文本,最后写了几个带超链接的单词。Web浏览器将切实遵守这些显示指令。然而,只有人类思维可以理解那个标题事实上是博文的题目,子标题是作者,斜体文本是文章的发表日期,而带超链接的单词是分类标签。程序与人类理解之间存在很大的鸿沟。
![]() |
如果浏览器从网页的可视化元素中接收到了关于其内在含义的信息会怎么样呢?一个在博客中宣布的宴会可以很容易地被拷贝到用户的日程表中,而作者的完整联系方式同样可以很容易地被拷贝到用户的地址簿中。用户可以根据分类标签(通常称作tag)自动地回访以前浏览过的文章。一张从网站上拷贝并粘贴到学校报告中的照片可以附带一个链回到摄影师的链接,并给与她合适的称赞以感谢知识产权。当原先为人类准备的Web数据被扩展为也能为计算机程序的处理提供便利时,这些程序将很明显地变得更有帮助,因为它们开始理解数据的结构了。
RDFa正允许XHTML作者这么做。使用一些简单的XHTML属性,作者能够用机器可读的指示符来标记人类可读的数据以便于浏览器和其它程序去解释。一张网页为各项目能够包括的标记可以像文章的题目那么简单,也可以像用户的完整社会网络那么复杂。
RDFa得益于RDF [RDF]的广泛能力——RDF是W3C为可互操作的机器可读数据建立的规范。当然,本文的读者们并不被要求去理解RDF。但读者们需要对XHTML有一个基本了解。
比如说Alice——一个在http://example.com/alice上既发布了专业文章又发布了个人文章的博客。我们将构造样例标记来演示Alice可以如何使用RDFa。这些样例的完整标记可以独立地在此阅读。
在她博客的页脚,Alice声明她的博客内容可以自由地被重用——只要在她的文章被引用时给予她应有的称赞就行了。在这个博客页面的XHTML中包括了一个指向Creative Commons [CC]许可协议的链接:
...
All content on this site is licensed under
<a href="http://creativecommons.org/licenses/by/3.0/">
a Creative Commons License
</a>.
一个人可以清楚地理解这个句子的意思,特别是其中的链接相对于当前文档的含义:它指出了文档的许可协议——即在什么条件下页面的内容可以被分发。不幸的是,当Bob访问Alice的博客时,他的浏览器只能明白这儿有一个普通的链接——可能指向Alice的一个朋友,也可能指向她的个人简历。为了让Bob的浏览器理解这个链接实际上是指向文档的许可协议条款,Alice需要增加一些调料来提示这个链接是什么类型的。
她可以使用rel属性来增加这个调料(我们将简写为@rel来避免经常重复“属性”这个词),它定义了当前页面与链接到的页面之间的关系。这个属性的值是license——一个在XHTML中恰为这个目的保留的关键词:
...
All content on this site is licensed under
<a rel="license" href="http://creativecommons.org/licenses/by/3.0/">
a Creative Commons License
</a>.
有了这样微小的升级,Bob的浏览器现在可以理解这个链接有了点调料:它指向这个博客的许可协议。
![]() |
Alice很高兴,因为她为XHTML增加调料后使得Bob很容易地找到了她博客的许可协议。但是文章的题目和作者的姓名怎么办?现在,Alice并不是要去标记一个链接,而是要扩展页面中的文本。文章的题目就是页面的标题,而她的姓名就是子标题:
<div> <h2>The trouble with Bob</h2> <h3>Alice</h3> ... </div>
为了表明h2代表页面的标题而h3代表作者,Alice用@property这个RDFa中引入的属性来特别地标记XHTML页面中的文本。
<div xmlns:dc="http://purl.org/dc/elements/1.1/"> <h2 property="dc:title">The trouble with Bob</h2> <h3 property="dc:creator">Alice</h3> ... </div>
为什么要用dc:creator和dc:title而不简单地用creator和title呢?这是因为XHTML并没有为这两个概念保留关键词。尽管Alice可以大胆地写成property="title",但一个程序读到这里如何才能知道这里的“title”到底是指一件作品的题目、一门职业的名称还是什么房产契约呢?并且,如果每个Web发布者都使用他们自己的短关键词作为属性,那么所有可用的属性构成的世界将变得相当混乱,就有点像在计算机中把每个文件都直接存在桌面上而不使用任何目录结构去组织它们。
为了进行一定程度的组织,RDFa并不去识别property="title"。Alice必须用一个URL来指明Web上的一个目录,从那儿导入她想要表达的特定的creator和title概念。幸运地是,Dublin Core [DC]社区已经为描述文档时会用到的概念定义了一个词汇表,其中就包括creator和title,并且title就是指一件作品的题目。因此,Alice就:
xmlns:dc="http://purl.org/dc/elements/1.1/"导入了Dublin Core词汇表,将前缀dc和http://purl.org/dc/elements/1.1/这个URL关联了起来,并且dc:creator和dc:title。它们分别是http://purl.org/dc/elements/1.1/creator和http://purl.org/dc/elements/1.1/title这两个URL的缩写。在RDFa中,事实上所有的属性名称都是URL。
![]() |
Alice的博客当然包括不止一篇博文。有时候Alice的姐妹Eve也会来客串写几篇。博客的头版列出了10篇最近的博文,每一篇显示了它的题目、作者和第一段。那么,当这些博文出现在一张网页上时,Alice应该怎样为它们分别标记题目呢?RDFa提供了@about属性来指定其中的RDFa标记应该作用于哪个URL:
<div xmlns:dc="http://purl.org/dc/elements/1.1/"> <div about="/alice/posts/trouble_with_bob"> <h2 property="dc:title">The trouble with Bob</h2> <h3 property="dc:creator">Alice</h3> ... </div> <div about="/alice/posts/jos_barbecue"> <h2 property="dc:title">Jo's Barbecue</h2> <h3 property="dc:creator">Eve</h3> ... </div> ... </div>
让我们再一次用示意图表示——把URL连到属性上:
![]() |
当Alice在博客中贴上了他的好朋友Bob拍的照片时,她可以用同样的方法去给他合适的称赞以感谢知识产权:
<div about="/alice/posts/trouble_with_bob">
<h2 property="dc:title">The trouble with Bob</h2>
The trouble with Bob is that he takes much better photos than I do:
<div about="http://example.com/bob/photos/sunset.jpg">
<img src="http://example.com/bob/photos/sunset.jpg" />
<span property="dc:title">Beautiful Sunset</span>
by <span property="dc:creator">Bob</span>.
</div>
</div>
注意最内层的@about属性值http://example.com/bob/photos/sunset.jpg是怎样对最内层的div内的所有标记“隐藏”了外层的@about属性值/alice/posts/trouble_with_bob的。再一次用示意图来抽象地表示这部分新的标记背后的数据:
![]() |
Alice还想要让她自己的一些信息(比如电子邮件地址、电话号码等)容易地被她朋友的联系信息管理软件所获取。这回就不再是描述一张网页的属性了,而是要描述她自己这样一个人的属性。因此,她增加了更深的结构使得她能够把多个具有各自属性的项连起来。
Alice已经在她的博客上显示了她的联系信息。
<div>
<p>
Alice Birpemswick
</p>
<p>
Email: <a href="mailto:alice@example.com">alice@example.com</a>
</p>
<p>
Phone: <a href="tel:+1-617-555-7332">+1 617.555.7332</a>
</p>
</div>
Dublin Core词汇表并没有提供用来描述联系信息的属性名称,但Friend-of-a-Friend [FOAF]词汇表提供了。在RDFa中,很经常也很容易在一个页面中组合使用不同的词汇表。现在Alice要导入FOAF词汇表并声明一个foaf:Person的实例。因此,Alice使用了@typeof这个RDFa属性,这个属性专门用来声明一个具有特定类型的数据项:
<div typeof="foaf:Person" xmlns:foaf="http://xmlns.com/foaf/0.1/">
...
接下来,Alice可以指明页面上的哪些内容表示她的全名、电子邮件地址和电话号码:
<div typeof="foaf:Person" xmlns:foaf="http://xmlns.com/foaf/0.1/"> <p property="foaf:name"> Alice Birpemswick </p> <p> Email: <a rel="foaf:mbox" href="mailto:alice@example.com">alice@example.com</a> </p> <p> Phone: <a rel="foaf:phone" href="tel:+1-617-555-7332">+1 617.555.7332</a> </p> </div>
注意Alice是怎样没有像她此前增加博文元数据时那样去指定@about的。那么,她正把这些属性关联到什么东西上面呢?事实上,div中的@typeof隐式地设定了div内标记的那些属性的主语。姓名、电子邮件地址和电话号码都关联到一个类型为foaf:Person的新的节点。这个节点没有任何URL来标识,所以被称作空白节点。
![]() |
接下来,Alice想要增加一些她好朋友的信息,至少包括他们的姓名和主页。普通的XHTML写法是:
<div>
<ul>
<li>
<a href="http://example.com/bob/">Bob</a>
</li>
<li>
<a href="http://example.com/eve/">Eve</a>
</li>
<li>
<a href="http://example.com/manu/">Manu</a>
</li>
</ul>
</div>
首先,Alice指明所有这些朋友的类型都是foaf:Person。
<div xmlns:foaf="http://xmlns.com/foaf/0.1/"> <ul> <li typeof="foaf:Person"> <a href="http://example.com/bob/">Bob</a> </li> <li typeof="foaf:Person"> <a href="http://example.com/eve/">Eve</a> </li> <li typeof="foaf:Person"> <a href="http://example.com/manu/">Manu</a> </li> </ul> </div>
除了声明数据的类型以外,每个@typeof都会创建一个新的有着自己独有属性的空白节点,因而这些节点都不需要URL来标识了。例如,Alice很容易地指明了每个朋友的主页(显然主页地址是一个人的独有属性,因此可以用来唯一地确定一个人):
<div xmlns:foaf="http://xmlns.com/foaf/0.1/">
<ul>
<li typeof="foaf:Person">
<a rel="foaf:homepage" href="http://example.com/bob/">Bob</a>
</li>
<li typeof="foaf:Person">
<a rel="foaf:homepage" href="http://example.com/eve/">Eve</a>
</li>
<li typeof="foaf:Person">
<a rel="foaf:homepage" href="http://example.com/manu/">Manu</a>
</li>
</ul>
</div>
当然,还有每个朋友的姓名:
<div xmlns:foaf="http://xmlns.com/foaf/0.1/">
<ul>
<li typeof="foaf:Person">
<a property="foaf:name" rel="foaf:homepage" href="http://example.com/bob/">Bob</a>
</li>
<li typeof="foaf:Person">
<a property="foaf:name" rel="foaf:homepage" href="http://example.com/eve/">Eve</a>
</li>
<li typeof="foaf:Person">
<a property="foaf:name" rel="foaf:homepage" href="http://example.com/manu/">Manu</a>
</li>
</ul>
</div>
Alice用@property来表明那些链接上的文本("Bob"、"Eve"和"Manu")事实上是她朋友们的姓名。通过@rel来表明那些可以点击的链接将链到她朋友们的主页。Alice很高兴只用了这么一点额外的标记就能够完全地描述一个既让人看起来舒服的页面同时又是一个机器可读的数据集。
此前,Alice对于重复地在每个新的社会网络站点上输入她朋友的信息已经感到厌倦了。而有了RDFa,她只需要在她自己的网页上指出她的朋友圈子,而让那些社会网络应用程序去自动地读取这些信息。目前为止,Alice已经列上去了三个人,但还没有指明她与他们之间的关系;他们可能是她的朋友,也可能是她最喜欢的17世纪的诗人。为了表明实际上她是认识他们的,她用了FOAF词汇表中的foaf:knows属性:
<div xmlns:foaf="http://xmlns.com/foaf/0.1/" about="#me" rel="foaf:knows">
<ul>
<li typeof="foaf:Person">
<a property="foaf:name" rel="foaf:homepage" href="http://example.com/bob">Bob</a>
</li>
<li typeof="foaf:Person">
<a property="foaf:name" rel="foaf:homepage" href="http://example.com/eve">Eve</a>
</li>
<li typeof="foaf:Person">
<a property="foaf:name" rel="foaf:homepage" href="http://example.com/manu">Manu</a>
</li>
</ul>
</div>
只要用一次rel="foaf:knows"就足以把Bob、Eve和Manu都连到Alice了。能做到这样是因为RDFa中链式的概念:因为顶层的@rel并没有相应的@href,所以它将连到任何它所包含的节点,在这里就是被@typeof定义的三个节点。(@about="#me"是一种FOAF/RDF约定:代表Alice这个人的URL是http://example.com/alice#me。它不应当与Alice的主页http://example.com/alice混淆。你这个人肯定不能与你的主页等同起来。)
![]() |
RDF,即资源描述框架,正是我们在上述例子中抽取出的图结构的抽象数据表示。图中的每个箭头被表示为一个“主语-谓语-宾语”形式的三元组:主语是箭头的起始节点,谓语是箭头本身,宾语是箭头的终止节点或是一个字面量。一个RDF数据集通常称作一个“RDF图”,并且一般存储在一个所谓“三元组存储器”中。
比如说第一个示例图:
![]() |
这个图中的两个RDF三元组用Notation3语法 [N3]书写如下:
<http://www.example.com/alice/posts/trouble_with_bob>
<http://purl.org/dc/elements/1.1/title> "The Trouble with Bob";
<http://purl.org/dc/elements/1.1/creator> "Alice" .
同样地,我们画出来的TYPE箭头与其它箭头没什么不同,只不过它们的标签实际上是一个核心的RDF属性——rdf:type,这里rdf命名空间是指<http://www.w3.org/1999/02/22-rdf-syntax-ns#>。上述联系信息的例子因此应该这样表示成RDF图:
![]() |
RDF的定位是为描述数据提供一个通用的语言。一个数据单元可以有任意多的域,域的名称是那些可以被任何数据发布者重用的URL,正如任何Web开发者都可以连接到任何网页,即使并不是他们自己创建的。有了来源不同的RDF三元组形式的数据,并使用RDF查询语言SPARQL [SPARQL],人们可以搜索“Alice的那些创建了题目包含'Bob'的项的朋友们”,不论那些项是博文、视频、日程表事件或者其它我们还不知道的数据类型。
RDF是一种抽象的、机器可读的数据表示,用来最大程度地实现词汇表的重用。RDFa是一种在XHTML中通过重用已有的人类可读的数据来描述RDF数据的方法。
Alice已经用RDFa标记了她的页面,接下来她可能又会发现需要去描述一些数据,例如她最喜欢的照片,而要描述这些数据,现有的Dublin Core或者FOAF等词汇表是不够的。既然RDFa不过是RDF的一种表示形式,支持RDF可扩展性的RDF模式机制同样也可以支持RDFa的可扩展性。一旦一个RDF词汇表被创建了,它就可以像现有的词汇表一样被用在RDFa标记中。
RDF入门[RDF-SCHEMA-PRIMER]的第5节介绍了如何去创建一个RDF模式。笼统一点说,为RDFa创建一个RDF模式包括:
http://example.com/photos/vocab#。Photo和Camera,以及属性takenWith来把一张照片关联到拍摄这张照片的照相机上。xmlns:photo="http://example.com/photos/vocab#"以及typeof="photo:Camera"。需要注意的是,任何人只要能在Web上发布一个文档就同样能够发布一个RDF词汇表并定义他们想要描述的新的数据域。RDF和RDFa允许词汇表以完全分布式的形式被扩展。
更多的例子、工具以及相关信息可以在RDFa维基中找到。
本文档是RDF-in-HTML工作组的工作,包括(按字母顺序)Ben Adida、Mark Birbeck、Jeremy Carroll、Michael Hausenblas、Shane McCarron、Steven Pemberton、Manu Sporny、Ralph Swick和Elias Torres。这项工作的完成得益于以下人员的帮助:语义部署工作组以及它的前身语义Web部署和最佳实践工作组,特别是Tom Baker和Guus Schreiber主席(以及前任主席David Wood);XHTML2工作组;语义Web行动的前任领导Eric Miller;以及语义Web行动的现任领导Ivan Herman。本文档的早期版本由Gary Ng和David Booth进行了正式审阅,更早的版本由Diego Berrueta和Ed Summers审阅,他们都提出了深刻的意见,对这项工作有极大的促进作用。Bob DuCharme也审阅了这项工作并提供了有用的意见。