Chrome 22 выводит недопустимый XML, если атрибуты имеют пространство имен xlink

у меня есть следующий минимальный фрагмент 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);

когда я выполняю код в большинстве браузеров (просто вставьте его в консоль JavaScript Вашего браузера), проанализированный затем сериализованный XML эквивалентен оригиналу. Например, на Chrome 8 я получаю:

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

однако в Chrome 22 тот же фрагмент кода изменяет XML на:

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

обратите внимание, что префикс пространства имен xlink используемые атрибуты title и 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);

Итак, здесь я изменил URL пространства имен на что-то менее известное, и вывод теперь действителен:

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

следующий фрагмент также отлично работает:

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

поэтому в этом случае мы используем префикс "expected" для пространства имен XLink, а затем сериализуем без проблем:

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

1 ответов


Я все еще уверен, что в Chrome есть ошибка XMLSerializer, скорее всего, введено при обращении к SVG обработка атрибутов XLink, которые Barbarrosa указал на. Но учитывая отсутствие реакции на сообщить об ошибке Я сделал для этого, мы должны были двигаться вперед и работать над проблемой.

мы работаем вокруг проблемы, вызывая эту функцию на 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]);
    }
  }
}

функция просто проверяет, что xmlns:xlink атрибут объявляется для любого элемента, который имеет атрибут в пространстве имен XLink. Поскольку функция пересекает дерево и, следовательно, может быть довольно трудоемкой, я вызываю ее только для версий Chrome 22 и выше.

обратите внимание, что в большинстве случаев вы также можете сойти с рук, просто добавив xmlns:xlink пространство имен в элементе документа, так как оно будет унаследовано оттуда. Но в нашем случае был какой-то другой код, который удаляет элемент документа с помощью регулярного выражения, поэтому мы решили чтобы играть в нее безопасно и просто добавить атрибут везде, где это может понадобиться.

обновление (20130324):

на ошибка был исправлен и проверен в Chrome Canary 26. Я смог проверить это сам на версии 25.0.1364.172 m тоже.