Как использовать jQuery для синтаксического анализа XML с пространствами имен

82

Я новичок в jQuery и хотел бы разобрать XML-документ.

Я могу анализировать обычный XML с пространствами имен по умолчанию, но с XML, например:

<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">
   <s:Schema id="RowsetSchema">
     <s:ElementType name="row" content="eltOnly" rs:CommandTimeout="30">
       <s:AttributeType name="ows_ID" rs:name="ID" rs:number="1">
        <s:datatype dt:type="i4" dt:maxLength="4" />
      </s:AttributeType>
       <s:AttributeType name="ows_DocIcon" rs:name="Type" rs:number="2">
        <s:datatype dt:type="string" dt:maxLength="512" />
      </s:AttributeType>
       <s:AttributeType name="ows_LinkTitle" rs:name="Title" rs:number="3">
        <s:datatype dt:type="string" dt:maxLength="512" />
      </s:AttributeType>
       <s:AttributeType name="ows_ServiceCategory" rs:name="Service Category" rs:number="4">
        <s:datatype dt:type="string" dt:maxLength="512" />
      </s:AttributeType>
    </s:ElementType>
  </s:Schema>
   <rs:data>
    <z:row ows_ID="2" ows_LinkTitle="Sample Data 1" />
    <z:row ows_ID="3" ows_LinkTitle="Sample Data 2" />
    <z:row ows_ID="4" ows_LinkTitle="Sample Data 3" />
  </rs:data>
</xml>

Все, что я действительно хочу, это <z:row> .

До сих пор я использовал:

$.get(xmlPath, {}, function(xml) {
    $("rs:data", xml).find("z:row").each(function(i) {
        alert("found zrow");
    });
}, "xml");

на самом деле не повезло. Есть идеи?

Брайан Лян
источник
Пропуск префикса пространства имен сработал для меня. См. Ответ: stackoverflow.com/a/25089647/2539811
Винсил Бишоп,

Ответы:

135

Я понял.

Оказывается, это требует \\выхода из толстой кишки.

$.get(xmlPath, {}, function(xml) {
    $("rs\\:data", xml).find("z\\:row").each(function(i) {
        alert("found zrow");
    });
}, "xml");

Как заметил Рич:

Лучшее решение не требует экранирования и работает во всех «современных» браузерах:

.find("[nodeName=z:row]")
Брайан Лян
источник
2
$('[nodeName=rs:data]', xml).find('[nodeName=z:row]')- работает с 1.3.2 под WebKit (где метод экранированного двоеточия явно не работает)
gnarf 06
2
похоже, это перестало работать в jQuery версии 1.4.4, что, как мне кажется, означает, что jQuery имеет лучшую поддержку пространства имен XML. Чтобы быть в безопасности, это работает,$('[nodeName=rs:data],data')
Джош Пирс,
15
Теперь jQuery 1.7 отсутствует, и это последнее решение больше не работает. Какой новый способ?
Gapipro
3
В jQuery 1.8.x это больше не работает. Это должно выполняться с помощью специального обходного пути совместимости с псевдоклассами, как описано здесь .
Miere
5
Несмотря на то, что это ответ на этот вопрос для данного XML - документ, я хотел бы, чтобы напомнить людям о том , что префиксы нравится rs, dtили sна самом деле не пространств имен. Пространства имен - это URN в верхней части файла. Префиксы - это просто псевдонимы, выбранные автором документа для краткости. Один и тот же документ с одинаковыми пространствами имен может быть создан с совершенно разными префиксами. Я рекомендую всем искать API, которые понимают пространства имен, вместо того, чтобы использовать префиксы в ваших запросах. Например, в DOM API браузера вы можете использовать getElementByTagNameNS()и getAttributeNS().
sergiopereira
35

Я безуспешно читал несколько часов о плагинах и всевозможных решениях.

ArnisAndy опубликовал ссылку на обсуждение jQuery, где предлагается этот ответ, и я могу подтвердить, что это работает для меня в Chrome (v18.0), FireFox (v11.0), IE (v9.08) и Safari (v5.1.5). ) с помощью jQuery (v1.7.2).

Я пытаюсь очистить канал WordPress, где контент называется <content: encoded>, и это то, что у меня сработало:

content: $this.find("content\\:encoded, encoded").text()
Фасани
источник
3
Это был единственный метод, который надежно работал у меня с использованием последней версии jQuery (той же версии), так что спасибо!
Dominic K
2
Это работает для меня , пока я использовал .each()цикл для перебора itemэлементов: $('dc\\:creator, creator', this).text(). Хотя я не уверен, зачем это , creatorбыло нужно, и dc\\:creatorне просто работало.
Филлип Пейтон
20

Если вы используете jquery 1.5, вам нужно будет добавить кавычки вокруг значения атрибута селектора узла, чтобы он работал:

.find('[nodeName="z:row"]')
s0laris
источник
19

Хотя приведенный выше ответ кажется правильным, он не работает в браузерах webkit (Safari, Chrome). Я считаю, что лучшим решением было бы:

.find("[nodeName=z:myRow, myRow]")    
Богатый
источник
5
похоже, это перестало работать в jQuery версии 1.4.4, что, как мне кажется, означает, что jQuery имеет лучшую поддержку пространства имен XML. Чтобы быть в безопасности, это работает$('[nodeName=rs:data],data')
Джош Пирс
16

Если кому-то нужно это сделать без jQuery , только с обычным Javascript и для Google Chrome (webkit) , это единственный способ заставить его работать после большого количества исследований и тестирования.

parentNode.getElementsByTagNameNS("*", "name");

Это будет работать для получения следующего узла: <prefix:name>. Как видите, префикс или пространство имен опущены, и они будут соответствовать элементам с разными пространствами имен при условии, что имя тега name. Но, надеюсь, для вас это не будет проблемой.

У меня ничего из этого не сработало (я разрабатываю расширение для Google Chrome):

getElementsByTagNameNS("prefix", "name")

getElementsByTagName("prefix:name")

getElementsByTagName("prefix\\:name")

getElementsByTagName("name")

редактировать : после некоторого сна я нашел рабочий обходной путь :) Эта функция возвращает первый узел, соответствующий полному, nodeNameнапример <prefix:name>:

// Helper function for nodes names that include a prefix and a colon, such as "<yt:rating>"
function getElementByNodeName(parentNode, nodeName)
{   
    var colonIndex = nodeName.indexOf(":");
    var tag = nodeName.substr(colonIndex + 1);
    var nodes = parentNode.getElementsByTagNameNS("*", tag);
    for (var i = 0; i < nodes.length; i++)
    {
        if (nodes[i].nodeName == nodeName) return nodes[i]
    }
    return undefined;
}

Его можно легко изменить, если вам нужно вернуть все совпадающие элементы. Надеюсь, поможет!

cprcrack
источник
14

Ни одно из приведенных выше решений не работает так хорошо. Я нашел это, и скорость была улучшена. просто добавьте это, работает как шарм:

$.fn.filterNode = function(name) {
    return this.find('*').filter(function() {
       return this.nodeName === name;
    });
};

Применение:

var ineedthatelementwiththepsuedo = $('someparentelement').filterNode('dc:creator');

источник: http://www.steveworkman.com/html5-2/javascript/2011/improving-javascript-xml-node-finding-performance-by-2000/

Ти Джей Тейт
источник
Спасибо за фрагмент - это очень полезно / решает проблему.
Гилман
3

Стоит отметить, что начиная с jQuery 1.7 были проблемы с некоторыми обходными путями для поиска элементов пространства имен. Смотрите эти ссылки для получения дополнительной информации:

ArnisAndy
источник
Если важна производительность, то лучшим решением будет выбрать теги без jQuery. Для сравнения см .: jsperf.com/node-vs-double-select/13
3

Нашел решение в комментарии: Разбор XML с пространствами имен с помощью jQuery $ (). Find

У меня сработало использование второй половины имени узла после двоеточия. Использовал .find ("lat") вместо .find ("geo \: lat"), и это сработало для меня.


Моя установка:

  • Хром 42
  • jQuery 2.1.3

Пример XML (фрагмент из Google Contacts API):

<entry>
  <id>http://www.google.com/m8/feeds/contacts/mstefanow%40gmail.com/base/0</id>
  <gd:email rel="http://schemas.google.com/g/2005#other" address="email@example.com" primary="true"/>
</entry>

Код парсинга:

var xmlDoc = $.parseXML( xml );
var $xml = $( xmlDoc );
var $emailNode = $xml.find( "email" );
$("#email").html($emailNode.attr("address"));

Plnkr: http://plnkr.co/edit/l8VzyDq1NHtn5qC9zTjf?p=preview

Марс Робертсон
источник
Рад, что смог помочь :)
Майк Грейс
2

jQuery 1.7 не работает со следующим:

$(xml).find("[nodeName=a:IndexField2]")

Одно из решений, которое я смог использовать в Chrome, Firefox и IE, - это использовать селекторы, которые работают в IE, и селекторы, которые работают в Chrome, исходя из того факта, что один способ работает в IE, а другой - в Chrome:

$(xml).find('a\\\\:IndexField2, IndexField2')

В IE это возвращает узлы с использованием пространства имен (Firefox и IE требуют пространства имен), а в Chrome селектор возвращает узлы на основе селектора без пространства имен. Я не тестировал это в Safari, но он должен работать, потому что он работает в Chrome.

СиэтлДайвер
источник
2

Мое решение (потому что я использую прокси-сервер Php) - заменить: namespace на _ ... чтобы больше не было проблем с пространством имен ;-)

Будь проще !

Томас Деко
источник
2

По состоянию на начало 2016 года для меня с jQuery 1.12.0 работает следующий синтаксис:

  • IE 11 (11.0.9600.18204, обновление 11.0.28, KB3134815): .find("z\\:row")
  • Firefox 44.0.2: .find("z\\:row")
  • Хром 44.0.2403.89м: .find("row")

Синтаксис .find("[nodeName=z:row]") не работает ни в одном из упомянутых выше браузеров. Я не нашел способа применить пространство имен в Chrome.

Собирая все вместе, следующий синтаксис работает во всех упомянутых выше браузерах: .find("row,z\\:row")

stefan.schwetschke
источник
1

Как упоминалось выше, с указанным выше решением существуют проблемы с текущими браузерами / версиями jQuery - предлагаемый плагин не работает полностью из-за проблем с регистром ( nodeNameкак свойство, иногда все в верхнем регистре). Итак, я написал следующую быструю функцию:

$.findNS = function (o, nodeName)
{
    return o.children().filter(function ()
    {
        if (this.nodeName)
            return this.nodeName.toUpperCase() == nodeName.toUpperCase();
        else
            return false;
    });
};

Пример использования:

$.findNS($(xml), 'x:row');
Майк Оливер
источник
учитывая проблемы с версией jQuery, это явно лучшее решение
MatteoSp
1

содержание: $this.find("content\\:encoded, encoded").text()

идеальное решение ...

сачинкондана
источник
1

Существует плагин jquery-xmlns для jQuery для работы с пространствами имен в селекторах.

Дима Фомин
источник
0

Я не видел документации по использованию JQuery для синтаксического анализа XML. JQuery обычно использует Дом браузера для просмотра HTML-документа, я не верю, что он читает сам html.

Вероятно, вам стоит взглянуть на встроенную обработку XML в самом JavaScript.

http://www.webreference.com/programming/javascript/definitive2/

Крис Брандсма
источник
3
Совершенно не согласен. jQuery упрощает обработку XML-ответа, единственная сложность, с которой вы столкнетесь, - это использование пространств имен xml.
Ричард Клейтон,
1
@Richard: при использовании Ajax jQuery действительно использует responseXMLсвойство встроенного XMLHttpRequestобъекта, который действительно является XML-документом. Однако jQuery (до 1.5, когда parseXMLбыл представлен) не имел возможности синтаксического анализа XML, поэтому Крис был прав.
Tim Down
0

просто заменил пространство имен пустой строкой. У меня отлично работает. Протестированное решение в браузерах: Firefox, IE, Chrome

Моей задачей было прочитать и разобрать EXCEL-файл через Sharepoint EXCEL REST API. XML-ответ содержит теги с пространством имен «x:».

Я решил заменить пространство имен в XML пустой строкой. Работает следующим образом: 1. Получить интересующий узел из XML-ответа 2. Преобразовать XML-ответ (документ) выбранного узла в строку 2. Заменить пространство имен пустой строкой 3. Преобразовать строку обратно в XML-документ

См. Схему кода здесь ->

function processXMLResponse)(xData)
{
  var xml = TOOLS.convertXMLToString("", "",$(xData).find("entry content")[0]);
  xml = xml.replace(/x:/g, "");            // replace all occurences of namespace
  xData =  TOOLS.createXMLDocument(xml);   // convert string back to XML
}

Для преобразования XML в String найдите решение здесь: http://www.sencha.com/forum/showthread.php?34553-Convert-DOM-XML-Document-to-string

Карл
источник
0

В качестве альтернативы вы можете использовать в своем проекте fast-xml-parser и преобразовать данные XML в объект JS / JSON. Затем вы можете использовать его как свойство объекта. Он не использует JQuery или другие библиотеки, но решит вашу задачу.

var xmlData = '<xml xmlns:s="uuid:BDC6E3F0-6DA3-11d1-A2A3-00AA00C14882" xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882" xmlns:rs="urn:schemas-microsoft-com:rowset" xmlns:z="#RowsetSchema">'
+'   <s:Schema id="RowsetSchema">'
+'     <s:ElementType name="row" content="eltOnly" rs:CommandTimeout="30">'
+'       <s:AttributeType name="ows_ID" rs:name="ID" rs:number="1">'
+'        <s:datatype dt:type="i4" dt:maxLength="4" />'
+'      </s:AttributeType>'
+'       <s:AttributeType name="ows_DocIcon" rs:name="Type" rs:number="2">'
+'        <s:datatype dt:type="string" dt:maxLength="512" />'
+'      </s:AttributeType>'
+'       <s:AttributeType name="ows_LinkTitle" rs:name="Title" rs:number="3">'
+'        <s:datatype dt:type="string" dt:maxLength="512" />'
+'      </s:AttributeType>'
+'       <s:AttributeType name="ows_ServiceCategory" rs:name="Service Category" rs:number="4">'
+'        <s:datatype dt:type="string" dt:maxLength="512" />'
+'      </s:AttributeType>'
+'    </s:ElementType>'
+'  </s:Schema>'
+'   <rs:data>'
+'    <z:row ows_ID="2" ows_LinkTitle="Sample Data 1" />'
+'    <z:row ows_ID="3" ows_LinkTitle="Sample Data 2" />'
+'    <z:row ows_ID="4" ows_LinkTitle="Sample Data 3" />'
+'  </rs:data>'
+'</xml>'

var jsObj = parser.parse(xmlData,{attrPrefix:"",ignoreTextNodeAttr: false});
document.write(JSON.stringify(jsObj.xml["rs:data"]["z:row"][0],null,4) + "<br>");
document.write(JSON.stringify(jsObj.xml["rs:data"]["z:row"][1],null,4) + "<br>");
document.write(JSON.stringify(jsObj.xml["rs:data"]["z:row"][2],null,4) + "<br>");
<script src="https://cdnjs.cloudflare.com/ajax/libs/fast-xml-parser/2.9.2/parser.min.js"></script>

Вы можете игнорировать пространства имен при синтаксическом разборе объекта js / json. В этом случае вы можете получить прямой доступ к as jsObj.xml.data.row.

for(var i=0; i< jsObj.xml.data.row.length; i++){
  console.log(jsObj.xml.data.row[i]);
}

Отказ от ответственности : я создал fast-xml-parser.

Амит Кумар Гупта
источник
-1

Для браузеров Webkit вы можете просто опустить двоеточие. Чтобы найти, <media:content>например, RSS-канал, вы можете сделать следующее:

$(this).find("content");
Доннапеп
источник
В последнем сафари он не поддерживает использование. работает только предыдущая версия.
Бэрион Ли