最近准备做一些 RSS 相关的工作,但是发现除了 RSS 协议之外官方还提供了个 Atom 协议,算是继承自 RSS 并且进行了一些扩展,相对要比 RSS 复杂很多,而网上也没找到中文资料,阅读文档的时候就想着看都看了,那就顺便(并不)翻译成中文吧。
1. 介绍
Atom 是一种基于 XML 的文档格式,主要用于描述 Feed 相关的信息列表。
Feeds 流由很多 entries(条目)组成,每个条目都包含一组可扩展的附加元数据,例如标题等等。
1.1 实例
一个简短的单项 Atom Feed 文档:
Example Feed
2003-12-13T18:30:02Z
John Doe
urn:uuid:60a76c80-d399-11d9-b93C-0003939e0af6
Atom-Powered Robots Run Amok
urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a
2003-12-13T18:30:02Z
Some text.
或者一个更广泛的单项 Atom Feed 文档:
dive into mark
A <em>lot</em> of effort
went into making this effortless
2005-07-31T12:29:29Z
tag:example.org,2003:3
Copyright (c) 2003, Mark Pilgrim
Example Toolkit
Atom draft-07 snapshot
tag:example.org,2003:3.2397
2005-07-31T12:29:29Z
2003-12-13T08:29:29-04:00
Mark Pilgrim
[email protected]
Sam Ruby
Joe Gregorio
[Update: The Atom draft is finished.]
1.2 命名空间与版本
本规范中描述的 XML 数据格式的 XML 命名空间 URI 是:
http://www.w3.org/2005/Atom
方便起见,可以简写为"Atom 1.0".
1.3 符号约定
该规范描述了两个工件的一致性:Atom Feed Documents 与 Atom Entry Documents。
另外也对 Atom 解析程序提出了一些要求。
该规范使用 "atom:"
命名空间前缀表示上一节中的命名空间。但这并不是规范中的标准,可以任意选择,你甚至可以用 "rss:"
,只要你喜欢。
本文档中的关键词 "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" 是被解释为 BCP_14, [RFC2119] 中描述的那样,以这些一致性目标为范围。
这里大概说下这几个关键词的意思:
- MUST, REQUIRED, SHALL:规范绝对要求
- MUST NOT, SHALL NOT:规范绝对禁止
- SHOULD, RECOMMENDED:除非有正当理由,否则推荐这么做
- SHOULD NOT, NOT RECOMMENDED:除非有正当理由,否则别这么做
- MAY, OPTIONAL:中立,可选
2. Atom 文档
本规范描述了两种 Atom 文档:Atom Feed Documents 和 Atom Entry Documents。
Atom Feed Documents 用于描述 Atom feed(信息流),其中包含一些关于 feed 的元数据,以及与之相关联的 Entry(条目)。它的根结点是 atom:feed
。
Atom Entry Documents 仅用于描述一个 Atom Entry(条目),根结点为atom:entry
。
namespace atom = ""
start = atomFeed | atomEntry
这两种 Atom 文档同样都遵守 XML Information Set 规范。使用 XML 1.0 序列化。媒体类型为 application/atom+xml
。Atom 文档必须是格式良好的 XML。
Atom 文档中的每个元素都有可能(MAY)包含一个xml:base
XML 属性,这个属性的定义在这里[W3C.REC-xmlbase-20010627],大概可以理解为 baseUrl,即定义了该属性的元素下面的 url 可以使用相对路径加上这个 baseUrl。
Atom 文档中的每个元素都有可能(MAY)包含一个xml:lang
XML属性,它表示该元素以及其子元素中内容的自然语言类型。
综上所述,Atom 通用属性可能会包含 xml:base
,xml:lang
,以及一些未定义的属性。因此我们做出如下定义。
atomCommonAttributes =
attribute xml:base { atomUri }?,
attribute xml:lang { atomLanguageTag }?,
undefinedAttribute*
另外,Atom 是一种可扩展的格式,关于如何进行扩展后面会详细介绍。
3. Atom 通用结构(Common Atom Constructs)
许多 Atom 的元素共享一些共同的结构。 本节定义了这些结构和它们的要求,以方便相应的元素定义的引用。
当一个元素被认定为是一种特殊的结构时,它就继承了本节中该结构定义中的相应要求。
请注意,在 Date 结构体或任何 IRI 中都必须(MUST)不能有任何空白。 一些XML的实现在默认情况下会在值周围插入空白,这样的实现会发出无效的 Atom 文档。
3.1 文本结构(Text Constructs)
一个文本结构包含人类可读的文本,通常内容比较少。文本结构的内容是对语言敏感的。
定义如下:
atomPlainTextConstruct =
atomCommonAttributes,
attribute type { "text" | "html" }?,
text
atomXHTMLTextConstruct =
atomCommonAttributes,
attribute type { "xhtml" },
xhtmlDiv
atomTextConstruct = atomPlainTextConstruct | atomXHTMLTextConstruct
3.1.1 “type” 属性
文本结构可以有一个 type
属性。 其值必须(MUST)是 text
、html
或 xhtml
中的一个。
如果未提供 type
属性,则必须视为 text
类型。下面分别介绍这三种类型。
3.1.1.1 Text
举个例子,下面是一个包含文本内容的 title:
Less: <
首先,text
类型的文本是决不能包含子元素的,该类型是为了直接呈现给用户的,需要保证其直接的可读性。
其次,Atom 的处理软件可能会删除其中的一些空白字符,通过某种文字排版技术进行排版。
3.1.1.2 HTML
再举个例子,下面是一个包含 HTML 内容的 title:
Less: <em> < </em>
同样,html
类型的文本也是决不能包含子元素的,应该将其视作 HTML 格式来读取并显示。
其中包含的任何标记都应被转义后再显示。事实上,html
标记内容应该(SHOULD)是可以直接在
3.1.1.3 XHTML
下面是一个包含 XHTML 内容的 title:
Less: <
xhtml
标记的内容必须是是一个单独的 XHTML div 元素,同时也应该适合作为 XHTML 进行处理。
元素本身并不应该被显示,应该按照处理程序转义之后再将其展示。
下面是另一个标准的例子:
...
This is XHTML content.
...
This is XHTML content.
...
下面的例子假设 XHTML 命名空间已经在此之前被绑定到到文档中的 “xh” 前缀。
...
This is XHTML content.
...
3.2 Person Constructs
Person Construct 是用于描述类似于人类、公司的元素。
atomPersonConstruct =
atomCommonAttributes,
(element atom:name { text }
& element atom:uri { atomUri }?
& element atom:email { atomEmailAddress }?
& extensionElement*)
Person construct 主要包含了三个属性:name,uri,email,下面分别看下。
3.2.1 “atom:name” 元素
atom:name
元素内容表示一个人类可读的人名。其内容是语言敏感的(Language-Sensitive)。
Person Construct 必须包含一个 name 元素。
3.2.2 “atom:uri” 元素
atom:uri
元素包含一个关于这个人的 IRI,Person Construct 中可能(MAY)包含一个 atom:uri
元素,但是其个数必须不能(MUST NOT)超过一个。
3.2.3 “atom:email” 元素
atom:email
元素代表关于这个人的邮箱地址,Person Construct 中可能(MAY)包含一个 atom:email
元素,但是其个数必须不能(MUST NOT)超过一个。
3.3 Date Constructs
Person Construct 是用于描述日期时间的元素。其格式必须遵守 [RFC3339] 中对 date-time
的定义。此外,必须使用大写字母 “T” 来分隔日期和时间,如果没有数字时区偏移,则必须使用大写字母 "Z" 结尾。
atomDateConstruct =
atomCommonAttributes,
xsd:dateTime
下面是一些正确的例子:
2003-12-13T18:30:02Z
2003-12-13T18:30:02.25Z
2003-12-13T18:30:02+01:00
2003-12-13T18:30:02.25+01:00
日期最好(SHOULD)尽可能的准确。
4. Atom 元素定义
4.1 Container Elements(容器元素)
4.1.1 “atom:feed” 元素
atom:feed
元素是 Atom Feed Document 的顶层元素,被用作于元数据以及与之相关的元素的容器。其内容包含一些元数据以及零个或多个 atom:entry
元素。
atomFeed =
element atom:feed {
atomCommonAttributes,
(atomAuthor*
& atomCategory*
& atomContributor*
& atomGenerator?
& atomIcon?
& atomId
& atomLink*
& atomLogo?
& atomRights?
& atomSubtitle?
& atomTitle
& atomUpdated
& extensionElement*),
atomEntry*
}
本规范未规定 “atom:entry” 的顺序,其顺序没有意义。
本规范对 atom:feed
元素的子元素做出如下定义:
- 必须(MUST)包含一个或多个
atom:author
元素,除非其子atom:entry
元素中至少包含一个atom:author
元素。 - 可能(MAY)包含任意数量的
atom:category
元素 - 可能包含任意数量的
atom:contributor
元素 - 必须不能(MUST NOT)包含超过一个的
atom:generator
元素 - 必须不能包含超过一个的
atom:icon
元素 - 必须不能包含超过一个的
atom:logo
元素 - 必须包含一个
atom:id
元素 - 应当(SHOULD)包含一个
atom:link
元素,且其中包含rel=”relf”
属性,用于表示该文档的首选URL。 - 必须不能包含超过一个 rel 属性值为
"alternate"
的atom:link
元素 - 除了上面说的之外,还可能额外附加一些
atom:link
元素 - 必须不能包含超过一个
atom:rights
元素 - 必须不能包含超过一个
atom:subtitle
元素 - 必须包含一个
atom:title
元素 - 必须包含一个
atom:updated
元素
如果一个 Atom Feed 文档中包含多个 atom:id
相同的 atom:entry
,那么他们的 atom:updated
中的时间戳应该是不同的。
处理程序可以自己选择显示方式,例如选择最新的 atom:entry
。
4.1.1.1 提供文本内容
根据经验,包含文本内容的 Feed 效果会更好,假如应用程序中包含一个文本搜索功能,需要依靠少量的文本进行搜索,那么文本内容就会比较有用。
因此,建议每个 atom:entry
都提供一个非空的 atom:title
元素,一个非空的 atom:content
元素,如果不提供 atom:content
可以提供一个非空的 atom:summary
元素。
4.1.2 “atom:entry” 元素
atom:entry
表示一个单独的条目,是其元数据以及相关数据的容器。
其可作为 atom:feed
元素的子元素出现,也可以作为一个独立的文档(即顶层元素)出现。
atomEntry =
element atom:entry {
atomCommonAttributes,
(atomAuthor*
& atomCategory*
& atomContent?
& atomContributor*
& atomId
& atomLink*
& atomPublished?
& atomRights?
& atomSource?
& atomSummary?
& atomTitle
& atomUpdated
& extensionElement*)
}
本规范同样没有规定 atom:entry
子元素的顺序,所以其顺序没有意义。
本规范对 atom:entry
元素的子元素做出如下定义:
- 必须包含一个或多个
atom:author
元素,除非atom:entry
包含一个包含atom:author
元素的atom:source
元素,或者atom:feed
元素中包含atom:author
元素。 - 可能包含任意数量的
atom:category
元素 - 必须不能包含超过一个的
atom:content
元素 - 可能包含任意数量的
atom:contributor
元素 - 必须包含一个
atom:id
元素 - 如果其中未包含
atom:content
元素,则必须包含至少一个属性rel=alternate
的atom:link
元素 - 必须不能包含超过一个属性
rel=alternate
的atom:link
元素 - 除了上面说的之外,还可能额外附加一些
atom:link
元素 - 必须不能包含超过一个的
atom:published
元素 - 必须不能包含超过一个的
atom:rights
元素 - 必须不能包含超过一个的
atom:source
元素 - 下面两种情况下,必须包含一个
atom:summary
元素:-
atom:content
元素中包含一个src
属性 -
atom:content
元素内容使用 Base64 加密,例如atom:content
的type
属性是一个MIME
媒体类型,但并不是 XML 类型,即并不是以text/
开始,且不以/xml
或+xml
结尾。
-
- 必须不能包含超过一个
atom:summary
元素 - 必须包含一个
atom:title
元素 - 必须包含一个
atom:updated
元素
4.1.3 "atom:content” 元素
atom:content
元素包含或者链接到条目的内容。atom:content
是语言敏感的。
atomInlineTextContent =
element atom:content {
atomCommonAttributes,
attribute type { "text" | "html" }?,
(text)*
}
atomInlineXHTMLContent =
element atom:content {
atomCommonAttributes,
attribute type { "xhtml" },
xhtmlDiv
}
atomInlineOtherContent =
element atom:content {
atomCommonAttributes,
attribute type { atomMediaType }?,
(text|anyElement)*
}
atomOutOfLineContent =
element atom:content {
atomCommonAttributes,
attribute type { atomMediaType }?,
attribute src { atomUri },
empty
}
atomContent = atomInlineTextContent
| atomInlineXHTMLContent
| atomInlineOtherContent
| atomOutOfLineContent
如上所示,atomContent
包含四种类型的内容。
4.1.3.1 “type” 属性
在 atom:content
元素中,type
属性可能是 text/html/xhtml 中的一种,否则它必须是 MIME 媒体类型,但不可以是组合类型。
如果既没有提供 type 属性也没有提供 src 属性,那么应该按照 text 类型来进行处理。
4.1.3.2 “src” 属性
atom:content
元素可能包含一个 src
属性,其值必须是一个 IRI,如果提供了 src 属性,则 atom:content
内容必须为空。
如果提供了 src 属性,则应当提供 type 属性,且 type 属性必须是 MIME 媒体类型而不是 text/html/xhtml。此时,type 的值并非对应 IRI 的真正类型,当访问该 IRI 时还是要以 IRI 返回的媒体类型为准。
4.1.3.3 处理模式
Atom 文档以及 Atom 处理程序必须遵守以下规则。
- 如果 type 值为 text,
atom:content
必须不能包含子元素,且内容将会被排版后直接显示,需要保证其可读性。 - 如果 type 值为 html,
atom:content
必须不能包含子元素且需要是能被处理的 HTML,需要将其解析显示。 - 如果 type 值为 xhtml,
atom:content
必须是单个 XHTMLdiv
且需要是能被处理的 XHTML。 - 如果 type 值是个 XML 媒体类型或者以
+xml
或者/xml
结尾,可能会包含子元素,且应当可以按照指定的格式进行处理,如果 src 属性未提供,一般可以认为atom:content
元素包含单个子元素,且该元素是指定格式的 XML 文档的根元素。 - 如果 type 值以
text/
开始,atom:content
必须不能包含子元素。 - 除了上面的 type 类型之外,
atom:content
内容必须是个合法的 Base64 加密数据。
4.1.3.4 例子
XHTML inline:
...
This is XHTML content.
...
This is XHTML content.
...
下面的例子假设 XHTML 命名空间已经在此之前被绑定到到文档中的 “xh” 前缀。
...
This is XHTML content.
...
4.2 Metadata 元素
4.2.1 "atom:author” 元素
atom:author
是上面介绍过的 Person 结构(Person Construct),被用来表示 feed 或者 entry 的作者。
atomAuthor = element atom:author { atomPersonConstruct }
如果 atom:entry
元素中未包含 atom:author
元素,那么可以用 atom:source
中的 atom:author
元素,如果上述位置都不包含 atom:author
元素,那么可以用 atom:feed
中的 atom:author
元素。
4.2.2 "atom:category” 元素
atom:category
描述了关于这个 entry 或者 feed 的种类信息。本规范并为对其内容作出任何规定。
atomCategory =
element atom:category {
atomCommonAttributes,
attribute term { text },
attribute scheme { atomUri }?,
attribute label { text }?,
undefinedContent
}
4.2.2.1 "term” 属性
term
属性表示 entry 或者 feed 所属种类的标识字符串。atom:category
元素必须包含一个 term
属性。
4.2.2.2 "scheme” 属性
scheme
属性是个表示分类方案的 IRI,atom:category
元素可能会包含它。
4.2.2.3 "label” 属性
label 属性提供了一个人类可读的标签用于显示在终端程序中,不应该对其中内容做转义,这不是标记,atom:category
元素可能会包含它。
4.2.3 "atom:contributor” 元素
atom:contributor
元素同样是个 Person 结构(Person Construct),表示该 entry 或者 feed 的贡献者。
4.2.4 "atom:generator” 元素
atom:generator
用于标识生成 feed 的代理,用于调试或者其他目的。
atomGenerator = element atom:generator {
atomCommonAttributes,
attribute uri { atomUri }?,
attribute version { text }?,
text
}
如果元素提供了内容,那么必须是人类可读的文本,不应该对其内容做转义。
atom:generator
元素可能会包含一个 uri
属性,其值必须为 IRI。
atom:generator
元素可能会包含一个 version
属性,表示代理的版本。
4.2.5 "atom:icon” 元素
atom:icon
元素内容是一个 IRI,是 feed 的标识图像。
atomIcon = element atom:icon {
atomCommonAttributes,
(atomUri)
}
这个图片的长宽比应当是一比一的,并且应当适合小尺寸展示。
4.2.6 "atom:id” 元素
atom:id 元素表示一个永久且普遍唯一的 entry 或 feed 标识符。
atomId = element atom:id {
atomCommonAttributes,
(atomUri)
}
内容必须是 IRI,请注意这里的 IRI 并不包含相对引用。
当 Atom 文档被迁移、升级、聚合、再版、输出或者导入,atom:id 都必须不能改变。
4.2.7 "atom:link” 元素
atom:link
元素定义了从 entry/feed 到 Web 的引用,本规范对该元素的内容未做任何定义。
atomLink =
element atom:link {
atomCommonAttributes,
attribute href { atomUri },
attribute rel { atomNCName | atomUri }?,
attribute type { atomMediaType }?,
attribute hreflang { atomLanguageTag }?,
attribute title { text }?,
attribute length { text }?,
undefinedContent
}
4.2.7.1 "href” 属性
href 属性包含了连接的 IRI,atom:link
元素必须包含一个 href 属性,其值必须是 IRI 引用。
4.2.7.2 "rel” 属性
atom:link
元素可能包含一个 rel 属性,用于标识 link 关系类型。如果未提供 rel 属性,则 link 元素必须被解释为 alternate 类型。
rel 属性的值必须是个符合 RFC3987 规范的非空的字符串。
本规范为 rel 定义了五个初始值:
- alternate:表示 href 属性值的 IRI 标识了元素内容所描述内容的资源的另一个替代版本。
- related:表示 href 属性值的 IRI 标识了与元素内容相关的资源。
- self:表示 href 属性值的 IRI 标识了与元素内容相同的资源。
- enclosure:表示 href 属性值的 IRI 可能标识一个大尺寸资源,可能需要特殊处理,此时应当提供 length 属性。
- vie:表示 href 属性值的 IRI 标识了元素的信息来源。
4.2.7.3 “type” 属性
在 link 元素中,type 属性是个建议性的媒体类型:它是一个关于表示类型的提示,当引用 href 属性的值时,是预期会返回的类型。
需要注意的是,改类型属性并不覆盖实际的媒体类型和表示方法。
Link 元素可能包含一个 type 属性,其值必须遵守 MIME 媒体类型规范。
4.2.7.4 "hreflang” 属性
hreflang 属性描述了 href 属性所指向资源的语言,当与 rel=”alternate”
结合使用时,意味着该条目的翻译版本。
4.2.7.5 "title” 属性
title 属性是关于这个 link 的人类可读的描述,不需要对其内容进行转义,Link 元素可能包含它。
4.2.7.6 "length” 属性
length 属性标识了连接内容的建议长度(以八位字节为单位),但并不能保证其实际长度,Link 元素中可能包含它。
4.2.8 "atom:logo” 元素
atom:logo
元素内容是一个 IRI,是 feed 的标识图像。
atomLogo = element atom:logo {
atomCommonAttributes,
(atomUri)
}
图像应的宽高比应该是2:1的。
4.2.9 "atom:published” 元素
atom:published
元素是个 Date Construct,表示 entry 生命周期中早期的事件的时间。
atomPublished = element atom:published { atomDateConstruct }
通常情况下,表示 entry 的创建时间或者首次可用时间。
4.2.10 "atom:rights” 元素
atom:rights
****元素是个 Text Construct(文本结构),表示 entry/feed 的版权信息。
atomRights = element atom:rights { atomTextConstruct }
atom:rights
****元素不应当是机器语言描述的许可信息。
如果 atom:entry
中不包含 atom:rights
元素,那么 atom:feed
中的 atom:rights
元素则适用于此 entry。
4.2.11 "atom:source” 元素
如果一个 atom:entry
从另一个 feed 中复制过来,那么源 atom:feed
中的元数据可能会被隐藏,并且加入到 atom:source
元素的子元素中去。
如果源 atom:feed
中包含 atom:author
, atom:contributor
, atom:rights
以及 atom:category
且这些子元素在源 atom:entry
中不存在,那么应该这几个数据应该被保留。
atomSource =
element atom:source {
atomCommonAttributes,
(atomAuthor*
& atomCategory*
& atomContributor*
& atomGenerator?
& atomIcon?
& atomId?
& atomLink*
& atomLogo?
& atomRights?
& atomSubtitle?
& atomTitle?
& atomUpdated?
& extensionElement*)
}
4.2.12 "atom:subtitle” 元素
atom:subtitle
元素是个人类可读的 Text Construct(文本构造器),被当做 feed 的描述或者子标题。
atomSubtitle = element atom:subtitle { atomTextConstruct }
4.2.13 "atom:summary” 元素
atom:summary
元素是个人类可读的 Text Construct(文本构造器),用于描述 entry 的摘要、摘录等。
atomSummary = element atom:summary { atomTextConstruct }
不建议让 atom:summary
元素与 atom:title
或 atom:content
重复。
4.2.14 "atom:title” 元素
atom:title
元素是个人类可读的 Text Construct(文本构造器),用于描述 entry/feed 的标题。
atomTitle = element atom:title { atomTextConstruct }
4.2.15 "atom:updated” 元素
atom:updated 是个 Date Construct(日期构造器) ,表示 entry/feed 的最近一次的修改时间。
atomUpdated = element atom:updated { atomDateConstruct }
发布者可能会随着时间推移修改这个元素的值。
结尾
Atom 协议总体就这么多,本人才疏学浅,如有出错请谅解,另外还有一些其他附加内容,奈何精力有限,且暂时对本人没有帮助,就没有对其翻译。
除了上面的文章外,我还在写一个 Kotlin 平台的 RSS 与 Atom 协议的序列化反序列化实现,过几天会完成,需要的可以关注我的更新。
附录
Atom 协议标准地址:*https://datatracker.ietf.org/doc/html/rfc4287*