最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Chrome 22 outputs invalid XML when attributes have an xlink namespace - Stack Overflow

programmeradmin2浏览0评论

I have the following minimal JavaScript fragment:

var xml = '<El a:title="T" a:href="H" xmlns:a="" />';
var dom = new DOMParser().parseFromString(xml, 'text/xml');
xml = new XMLSerializer().serializeToString(dom);

When I execute the code in most browsers (just paste it into your browser's JavaScript console), the parsed-then-serialized XML is equivalent to the original. For example on Chrome 8 I get:

<El xmlns:a="" a:title="T" a:href="H"/>

However on Chrome 22 the same code fragment changes the XML to:

<El xmlns:a="" xlink:title="T" xlink:href="H"/>

Note that the namespace prefix xlink used by the title and href attributes is not defined anywhere, so the XML is now invalid. As you probably can imagine this causes all kinds of problems for code that tries to subsequently use the XML.

Is this a bug in the XMLSerializer or am I missing some intricacies about how the DOM should be serialized?

Also did anyone find a workaround that I can put in code, as opposed to make the XML match the apparent preference to use xlink as the prefix for the XLink namespace?

Update

I did some additional testing and the problem seems to be caused by the fact that the XMLSerializer recognizes the XLink namespace and insists on outputting an xlink prefix for it, without properly registering that prefix.

So this fragment work fine:

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);

So here I changed the Namespace URL to something less well-known and the output is now valid:

<El xmlns:a="any-other-namespace-uri" a:title="T" a:href="H"/>

The following fragment also works fine:

var xml = '<El a:title="T" a:href="H" xmlns:a="" />';
var dom = new DOMParser().parseFromString(xml, 'text/xml');
xml = new XMLSerializer().serializeToString(dom);

So in this case we use the "expected" prefix for the XLink namespace and it then serializes without problems:

<El xmlns:a="" a:title="T" a:href="H"/>

I have the following minimal JavaScript fragment:

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

When I execute the code in most browsers (just paste it into your browser's JavaScript console), the parsed-then-serialized XML is equivalent to the original. For example on Chrome 8 I get:

<El xmlns:a="http://www.w3/1999/xlink" a:title="T" a:href="H"/>

However on Chrome 22 the same code fragment changes the XML to:

<El xmlns:a="http://www.w3/1999/xlink" xlink:title="T" xlink:href="H"/>

Note that the namespace prefix xlink used by the title and href attributes is not defined anywhere, so the XML is now invalid. As you probably can imagine this causes all kinds of problems for code that tries to subsequently use the XML.

Is this a bug in the XMLSerializer or am I missing some intricacies about how the DOM should be serialized?

Also did anyone find a workaround that I can put in code, as opposed to make the XML match the apparent preference to use xlink as the prefix for the XLink namespace?

Update

I did some additional testing and the problem seems to be caused by the fact that the XMLSerializer recognizes the XLink namespace and insists on outputting an xlink prefix for it, without properly registering that prefix.

So this fragment work fine:

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);

So here I changed the Namespace URL to something less well-known and the output is now valid:

<El xmlns:a="any-other-namespace-uri" a:title="T" a:href="H"/>

The following fragment also works fine:

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

So in this case we use the "expected" prefix for the XLink namespace and it then serializes without problems:

<El xmlns:a="http://www.w3/2000/xlink" a:title="T" a:href="H"/>
Share Improve this question edited Nov 11, 2012 at 13:25 Frank van Puffelen asked Oct 9, 2012 at 15:28 Frank van PuffelenFrank van Puffelen 600k85 gold badges889 silver badges859 bronze badges 2
  • 4 I'm not entirely sure what the answer is, but this issue may be related: stackoverflow./questions/8979267/… – Barbarrosa Commented Oct 22, 2012 at 3:41
  • Thanks for the pointer Barbarrosa. I had seen reports about Chrome's SVG/XLink handling. But I actually fear that the "fix" for that may have been what caused the problem I am experiencing. With your link I may actually get one step closer to the offending code, so thanks! – Frank van Puffelen Commented Oct 22, 2012 at 12:09
Add a ment  | 

1 Answer 1

Reset to default 7

I am still quite certain that there is a bug in Chrome's XMLSerializer, most likely introduced while addressing the SVG handling of XLink attributes that Barbarrosa pointed to. But given the lack of response to the bug report I made for it, we've had to move forward and work around the problem.

We work around the problem by calling this function on the 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/1999/xlink")
        {
          usesXLinkNamespaceUri = true;
        }
      }
    }
    if (usesXLinkNamespaceUri && !hasXLinkNamespacePrefixDefined)
    {
      element.setAttribute('xmlns:xlink', 'http://www.w3/1999/xlink');
    }

    for (i = 0; i < element.childNodes.length; i++)
    {
      EnsureXLinkNamespaceOnElement(element.childNodes[i]);
    }
  }
}

The function simply ensures that the xmlns:xlink attribute is declared on any element that has attributed in the XLink namespace. Since the function traverses the tree and thus can be quite time-consuming, I only invoke it for Chrome versions 22 and higher.

Note that in most cases you could also get away with simply adding the xmlns:xlink namespace on the document element, since it will be inherited from there. But in our case there was some other code that strips the document element using a regular expression, so we decided to play it safe and simply add the attribute everywhere it may be needed.

Update (20130324):

The bug was fixed and verified in Chrome Canary 26. I have been able to verify it myself on version 25.0.1364.172 m too.

发布评论

评论列表(0)

  1. 暂无评论