当属性具有xlink命名空间时,Chrome 22会输出无效的XML

Fra*_*len 23 javascript google-chrome xmlserializer

我有以下最小的JavaScript片段:

var xml = '<El a:title="T" a:href="H" xmlns:a="http://www.w3.org/1999/xlink" />';
var dom = new DOMParser().parseFromString(xml, 'text/xml');
xml = new XMLSerializer().serializeToString(dom);
Run Code Online (Sandbox Code Playgroud)

当我在大多数浏览器中执行代码时(只需将其粘贴到浏览器的JavaScript控制台中),解析后序列化的XML与原始XML相同.例如在Chrome 8上,我得到:

<El xmlns:a="http://www.w3.org/1999/xlink" a:title="T" a:href="H"/>
Run Code Online (Sandbox Code Playgroud)

但是在Chrome 22上,相同的代码片段将XML更改为:

<El xmlns:a="http://www.w3.org/1999/xlink" xlink:title="T" xlink:href="H"/>
Run Code Online (Sandbox Code Playgroud)

请注意,xlinktitle和href属性使用的名称空间前缀未在任何位置定义,因此XML现在无效.正如您可能想象的那样,这会导致尝试随后使用XML的代码出现各种问题.

这是XMLSerializer中的错误还是我错过了关于如何序列化DOM的一些错综复杂的内容?

还有没有人找到我可以放在代码中的解决方法,而不是使XML匹配明显的首选项xlink用作XLink命名空间的前缀?

更新

我做了一些额外的测试,问题似乎是因为XMLSerializer识别XLink命名空间并坚持xlink为它输出前缀,而没有正确注册该前缀.

所以这个片段工作正常:

var xml = '<El a:title="T" a:href="H" xmlns:a="any-other-namespace-uri" />';
var dom = new DOMParser().parseFromString(xml, 'text/xml');
xml = new XMLSerializer().serializeToString(dom);
Run Code Online (Sandbox Code Playgroud)

所以在这里我将名称空间URL更改为不太知名的东西,输出现在有效:

<El xmlns:a="any-other-namespace-uri" a:title="T" a:href="H"/>
Run Code Online (Sandbox Code Playgroud)

以下片段也可以正常工作:

var xml = '<El a:title="T" a:href="H" xmlns:a="http://www.w3.org/2000/xlink" />';
var dom = new DOMParser().parseFromString(xml, 'text/xml');
xml = new XMLSerializer().serializeToString(dom);
Run Code Online (Sandbox Code Playgroud)

因此,在这种情况下,我们使用XLink命名空间的"预期"前缀,然后序列化没有问题:

<El xmlns:a="http://www.w3.org/2000/xlink" a:title="T" a:href="H"/>
Run Code Online (Sandbox Code Playgroud)

Fra*_*len 7

我仍然非常肯定Chrome中存在一个错误XMLSerializer,很可能是在解决Barbarrosa指出的XLink属性SVG处理时引入的.但鉴于我对它所做的错误报告缺乏回应,我们不得不前进并解决问题.

我们通过在以下方面调用此函数来解决此问题documentElement:

function EnsureXLinkNamespaceOnElement(element)
{
  if (element.nodeType == 1)
  {
    var usesXLinkNamespaceUri = false;
    var hasXLinkNamespacePrefixDefined = false;
    for (var i = 0; i < element.attributes.length; i++) 
    {
      var attribute = element.attributes[i];
      if (attribute.specified)
      {
        if (attribute.name.indexOf("xmlns:xlink") == 0) 
        {
          hasXLinkNamespacePrefixDefined = true;
        } 
        else if (attribute.namespaceURI == "http://www.w3.org/1999/xlink")
        {
          usesXLinkNamespaceUri = true;
        }
      }
    }
    if (usesXLinkNamespaceUri && !hasXLinkNamespacePrefixDefined)
    {
      element.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink');
    }

    for (i = 0; i < element.childNodes.length; i++)
    {
      EnsureXLinkNamespaceOnElement(element.childNodes[i]);
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

该函数只是确保xmlns:xlink在XLink命名空间中归属的任何元素上声明属性.由于该函数遍历树,因此可能非常耗时,因此我仅针对Chrome版本22及更高版本调用它.

请注意,在大多数情况下,您只需xmlns:xlink在文档元素上添加命名空间即可,因为它将从那里继承.但在我们的例子中,有一些其他代码使用正则表达式去除文档元素,因此我们决定安全地使用它,只需在可能需要的任何地方添加属性.

更新(20130324):

错误已在Chrome Canary 26中修复和验证.我已经能够在版本上验证它25.0.1364.172 m.