日期:2014-05-16  浏览次数:20378 次

写了一个XML Base的JS实现(简介篇)
最近想在一个小应用中采用浏览器端的xinclude。找了一下,居然没有找到现成的实现。所以就打算自己写一个。

完整的xinclude实现也需要用到一些其它技术。最基本的就是xml base。

在html中你的链接如果不是absolute url形式,就需要相对于文档位置或者文档中用base元素定义的base url来resolve。

xml base和html的base有点类似,但是更加灵活,通过xml:base属性而不是单独一个属性来设置,所以可以有层次。例如:

<?xml version="1.0"?>
<doc xml:base="http://example.org/today/"
     xmlns:xlink="http://www.w3.org/1999/xlink">
  <head>
    <title>Virtual Library</title>
  </head>
  <body>
    <paragraph>See <link xlink:type="simple" xlink:href="new.xml">what's
      new</link>!</paragraph>
    <paragraph>Check out the hot picks of the day!</paragraph>
    <olist xml:base="/hotpicks/">
      <item>
        <link xlink:type="simple" xlink:href="pick1.xml">Hot Pick #1</link>
      </item>
      <item>
        <link xlink:type="simple" xlink:href="pick2.xml">Hot Pick #2</link>
      </item>
      <item>
        <link xlink:type="simple" xlink:href="pick3.xml">Hot Pick #3</link>
      </item>
    </olist>
  </body>
</doc>


这个示例代码抄自于xml base规范。其中的xlink解析后如下:
    *      "what's new" resolves to the URI "http://example.org/today/new.xml"
    *      "Hot Pick #1" resolves to the URI "http://example.org/hotpicks/pick1.xml"
    *      "Hot Pick #2" resolves to the URI "http://example.org/hotpicks/pick2.xml"
    *      "Hot Pick #3" resolves to the URI "http://example.org/hotpicks/pick3.xml"

原理还是一目了然的。但是要做出一个完全符合标准,并且鲁棒的实现也不是很容易的。

首先是确定我们的API。

API决定了我们如何利用xml base。

本身xml base是很简单的,你可以直接在xml文档中加入xml:base属性即可。然后就是application的问题了。比如你使用xlink,或者xinclude,或者其它属性(比如各种xml格式中常见的链接)——关键是应用是否能利用它来算出每个链接(有可能是relative URI)的最后的absolute形式。

所以API就呼之欲出了。

对于xml文档中的一个uri,其要么是在文本(text node或cdata中),要么是在属性中(实际上还有其它,如entity,PI等,我们暂时忽略)。那么我们的参数就是uri,和uri所在的node,我们希望得到的,就是最后的absolute uri。如下:

resolveURI(node, uri)

从node开始,我们可以根据xml base算出其 base URI。所谓base URI,是URI规范(RFC2369)中的术语,relative URL是根据base URI进行resolve的。xml base中得到一个node的base URI的大体算法如下:

如果这个node是text node或cdata,其base URI就是该node的parentNode,也就是一个element的base URI。

如果这个node是attr,其base URI就是该attr所属的element。

如果这个node是element,那么如果它有xml:base属性,那base URI就是xml:base属性所表达的URI——注意xml:base属性也可以是relative URI,所以它自身也要根据更上一级的base URI来resolve。如果没有xml:base属性,那其base URI就是父元素(或者最上层的document节点)的base URI。注意,有时候可能没有父节点,比如这个node刚创建出来还没有插入DOM树,或者已经被从DOM树上remove了,这个时候base URI就是null。

如果这个node是document,则其值为documentURI,通常是document的资源地址,但是有时候是null,例如你是从内存中产生的,或者从字符串parse出来的。

这个算法其实比较简单,就是递归向上找。

实际上,DOM中已经有这样一个baseURI属性了。

DOM Level 3增加了Node.baseURI属性,用来返回一个节点的baseURI。所谓baseURI,是由XML infoset所规定的一个xml属性,其计算方式就是根据xml base规范来的(你可以看到,w3c的规范是一个规范集合,互相独立但是又紧密关联),也就是我上面描述的过程。

Firefox 2.0虽然没有完全支持DOM Level 3,但是它是支持xml base规范和baseURI属性的。Safari 3也是支持的(虽然其支持有些问题,后述)。

显然,你料到了,IE 6不支持baseURI。

其它浏览器,如IE 7,Opera等,我暂时未及测试。有兴趣的同志可以测一下。

为了兼容IE 6和其它不支持baseURI属性的,我又定义了

baseURI(node)

其含义等价于DOM Level 3的 Node.baseURI。

API好了之后,我们就可以实现它了(当然其实应该先写测试用例,呵呵)。

代码明天再发。