У меня есть фрагмент XML, который я анализирую с помощью jQuery parseXML. Большинство узлов не имеют префиксов, они находятся в пространстве имен по умолчанию, а некоторые имеют префиксы.

Мне нужно, чтобы все узлы в пространствах имен по умолчанию были связаны с префиксом. Я убедился, что этот префикс уже объявлен в строковой версии XML с магической заменой строки (т.е. xmlns:my="http://mydefaulns.com" объявляется на корневом уровне при загрузке XML).

Я попробовал следующее:

var defaultNs="http://mydefaulns.com";
var xmlDoc = $.parseXML(stringXML);
$(xmlDoc).find("*").each(function() {
    if (this.namespaceURI=== defaultNs) {
        this.prefix = "my";
    }
}

Но это никак не сказывается, когда я пишу XML обратно, префикса по-прежнему нет.

Я также попытался просто загрузить XML и вызвать:

xmlDoc.firstChild.removeAttribute("xmlns")

Но атрибут не был удален, поэтому префиксы не были волшебным образом обновлены.

В этот момент я думаю, что единственный способ получить желаемый результат - это воссоздать все узлы с новым префиксным именем, скопировав все атрибуты.

Это кажется действительно экстремальным, есть ли другой способ?

Ввод (строка):

<abc xmlns="http://mydefaulns.com" xmlns:my="http://mydefaulns.com"xmlns:other="http://other.com">
    <node1>Value</node1>
    <other:node2>Value2</other:node2>
</abc>

Желаемый результат:

<my:abc xmlns:my="http://mydefaulns.com"xmlns:other="http://other.com">
    <my:node1>Value</my:node1>
    <other:node2>Value2</other:node2>
</my:abc>

Фактический XML более сложный, но это дает вам представление.

Я анализирую XML с помощью jQuery.parse и возвращаю строковую версию, используя

function XMLDocumentToString(oXML) {
    if (typeof oXML.xml != "undefined") {
       return oXML.xml;
    } else if (XMLSerializer) {
        return (new XMLSerializer().serializeToString(oXML));
    } else {
        throw "Unable to serialize the XML";
    }    
 }
10
Melanie 10 Янв 2017 в 02:13

3 ответа

Лучший ответ

В этот момент я думаю, что единственный способ получить желаемый результат - это воссоздать все узлы с новым префиксным именем, скопировав все атрибуты.

Да, это именно то, что вы должны сделать, если вы хотите сделать это чисто.

Решения с использованием регулярных выражений являются хрупкими. Вот пример, который вы дали:

<abc xmlns="http://mydefaulns.com" xmlns:my="http://mydefaulns.com" xmlns:other="http://other.com">
    <node1>Value</node1>
    <other:node2>Value2</other:node2>
</abc>

Теперь рассмотрим следующий документ, который эквивалентен вашему оригинальному:

<abc xmlns="http://mydefaulns.com" xmlns:my="http://mydefaulns.com">
    <node1>Value</node1>
    <node2 xmlns="http://other.com">Value2</node2>
</abc>

Единственное, что изменилось, это то, как элементу node2, который находится в пространстве имен http://other.com, было присвоено пространство имен. В исходном документе это было через префикс other, который был определен в корневом узле. Здесь это путем переопределения пространства имен по умолчанию на node2. С точки зрения XML, два документа одинаковы . Неважно, как определяется пространство имен node2. Проблема в том, что ни один из двух полученных вами ответов на основе регулярных выражений не преобразует этот документ должным образом.

Вот реализация, которая манипулирует деревом DOM для получения конечного результата:

var stringXML = '<abc xmlns="http://mydefaulns.com" xmlns:my="http://mydefaulns.com" xmlns:other="http://other.com">\n\
    <node1>Value</node1>\n\
    <other:node2>Value2</other:node2>\n\
</abc>';

var defaultNS = "http://mydefaulns.com";
var xmlDoc = $.parseXML(stringXML);

xmlDoc.firstChild.removeAttribute("xmlns");

// Make sure we do have xmlns:my defined.
xmlDoc.firstChild.setAttribute("xmlns:my",defaultNS);

function transformChildren(parent) {
  // We take a copy of childNodes before clearing it.
  var childNodes = Array.prototype.slice.call(parent.childNodes);

  while (parent.firstChild) {
    parent.removeChild(parent.firstChild);
  }

  var newChild;
  var limit = childNodes.length;
  for (var childIx = 0; childIx < limit; ++childIx) {
    newChild = undefined;
    var node = childNodes[childIx];
    if (node.nodeType === Node.ELEMENT_NODE && node.namespaceURI === defaultNS) {
      newChild = xmlDoc.createElementNS(defaultNS, "my:" + node.tagName);

      // Copy the attributes.
      var attributes = node.attributes;
      for (var attrIx = 0; attrIx < attributes.length; ++attrIx) {
        var attr = attributes[attrIx];
        newChild.setAttributeNS(attr.namespaceURI, attr.name, attr.value)
      }

      // Move the children.
      while (node.firstChild) {
        newChild.appendChild(node.firstChild);
      }
      
      transformChildren(newChild);
    }
    
    parent.appendChild(newChild || node);
  }
}

transformChildren(xmlDoc);

// This is just reused from the question.
function XMLDocumentToString(oXML) {
  if (typeof oXML.xml != "undefined") {
    return oXML.xml;
  } else if (XMLSerializer) {
    return (new XMLSerializer().serializeToString(oXML));
  } else {
    throw "Unable to serialize the XML";
  }
}

console.log(XMLDocumentToString(xmlDoc));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
3
Louis 17 Янв 2017 в 09:34

Не нужно разбирать строку XML, просто используйте replace с regular expressions, например:

var prefix = "my:";
stringXML = stringXML.replace(/(<\/?)(\w+)(?!:)(\b)/g, "$1" + prefix + "$2$3");

Это будет соответствовать любой последовательности букв (действительное имя тега) сразу после < или </ (< необходимо, / необязательно) и за ним не следует {{ X4}}, но затем \b.

var stringXML = '<abc xmlns="http://mydefaulns.com" xmlns:my="http://mydefaulns.com" xmlns:other="http://other.com"><node1>Value</node1><other:node2>Value2</other:node2></abc>';

console.log("BEFORE: ", stringXML);

var prefix = "my:";
stringXML = stringXML.replace(/(<\/?)(\w+)(?!:)(\b)/g, "$1" + prefix + "$2$3");

console.log("AFTER: ", stringXML);
5
ibrahim mahrir 18 Янв 2017 в 23:25

Это может помочь вам

var inputXmlText='<abc xmlns="http://mydefaulns.com" xmlns:my="http://mydefaulns.com" xmlns:other="http://other.com"><node1>Value</node1><other:node2>Value2</other:node2></abc>'
var inputXml=jQuery.parseXML(inputXmlText);
var list=[];
var prefix="my";

$(inputXml).find("*").each(function(){
  if(this.tagName.indexOf(":")<0){
     inputXmlText=inputXmlText.replace(new RegExp("<" + this.tagName,'g') ,"<"+prefix+":" + this.tagName);
     inputXmlText=inputXmlText.replace(new RegExp("</" + this.tagName,'g') ,"</"+prefix+":" + this.tagName);  
  } 
});

console.log(inputXmlText);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
1
fingerpich 12 Янв 2017 в 20:56