Jak używać jQuery do analizowania XML z przestrzeniami nazw

82

Jestem nowy w jQuery i chciałbym przeanalizować dokument XML.

Jestem w stanie przeanalizować zwykły XML z domyślnymi przestrzeniami nazw, ale z XML, takimi jak:

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

Wszystko, czego naprawdę chcę, to <z:row> .

Do tej pory używałem:

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

bez powodzenia. Jakieś pomysły?

Brian Liang
źródło
Pominięcie przedrostka przestrzeni nazw zadziałało dla mnie. Zobacz tę odpowiedź: stackoverflow.com/a/25089647/2539811
Vincil Bishop

Odpowiedzi:

135

Mam to.

Okazuje się, że wymaga \\ucieczki z okrężnicy.

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

Jak wskazał Rich:

Lepsze rozwiązanie nie wymaga ucieczki i działa na wszystkich "nowoczesnych" przeglądarkach:

.find("[nodeName=z:row]")
Brian Liang
źródło
2
$('[nodeName=rs:data]', xml).find('[nodeName=z:row]')- działa z 1.3.2 pod WebKitem (gdzie metoda ucieczki dwukropka najwyraźniej nie działa)
gnarf
2
wydaje się, że przestało to działać w jQuery w wersji 1.4.4, co moim zdaniem oznacza, że ​​jQuery ma lepszą obsługę przestrzeni nazw XML. Aby być bezpiecznym, to działa$('[nodeName=rs:data],data')
Josh Pearce
15
Teraz jQuery 1.7 jest niedostępny i to ostatnie rozwiązanie już nie działa. Jaka jest nowa droga?
Gapipro
3
W jQuery 1.8.x już nie działa. Należy to osiągnąć przy użyciu niestandardowego obejścia kompatybilności pseudoklas, jak wyjaśniono tutaj .
Miere
5
Nawet jeśli to jest odpowiedź na pytanie dla danego dokumentu XML, chciałbym przypomnieć ludziom, że przedrostki podoba rs, dtczy snie są naprawdę nazw. Przestrzenie nazw to URN w górnej części pliku. Przedrostki to po prostu aliasy wybrane przez autora dokumentu, aby zachować zwięzłość. Ten sam dokument, pasujący do tych samych przestrzeni nazw, można utworzyć z zupełnie różnymi prefiksami. Zachęcam wszystkich do szukania interfejsów API, które rozumieją przestrzenie nazw, zamiast zakładać prefiksy w zapytaniach. Np. W przeglądarce DOM API możesz używać getElementByTagNameNS()i getAttributeNS().
sergiopereira
35

Spędziłem kilka godzin na czytaniu o wtyczkach i wszelkiego rodzaju rozwiązaniach bez powodzenia.

ArnisAndy opublikował łącze do dyskusji jQuery, w której jest oferowana ta odpowiedź i mogę potwierdzić, że działa to dla mnie w Chrome (wersja 18.0), FireFox (wersja 11.0), IE (wersja 9.08) i Safari (wersja 5.1.5) ) przy użyciu jQuery (v1.7.2).

Próbuję zeskrobać kanał WordPress, w którym zawartość nosi nazwę <content: encoded> i to właśnie zadziałało:

content: $this.find("content\\:encoded, encoded").text()
Fasani
źródło
3
To był jedyny, który niezawodnie działał dla mnie przy użyciu najnowszej wersji jQuery (ta sama wersja), więc dziękuję!
Dominic K
2
Ten pracował dla mnie podczas gdy użyłem .each()pętli do iterację itemelementów: $('dc\\:creator, creator', this).text(). Chociaż nie jestem pewien, dlaczego ten dodatek , creatorbył potrzebny i dc\\:creatornie tylko działał.
Fillip Peyton
20

Jeśli używasz jquery 1.5, będziesz musiał dodać cudzysłowy wokół wartości atrybutu selektora węzła, aby działało:

.find('[nodeName="z:row"]')
s0laris
źródło
19

Chociaż powyższa odpowiedź wydaje się być poprawna, nie działa ona w przeglądarkach webkit (Safari, Chrome). Uważam, że lepszym rozwiązaniem byłoby:

.find("[nodeName=z:myRow, myRow]")    
Bogaty
źródło
5
wydaje się, że przestało to działać w jQuery w wersji 1.4.4, co moim zdaniem oznacza, że ​​jQuery ma lepszą obsługę przestrzeni nazw XML. Aby być bezpiecznym, to działa$('[nodeName=rs:data],data')
Josh Pearce
16

Na wypadek, gdyby ktoś musiał to zrobić bez jQuery , tylko z normalnym Javascriptem i dla Google Chrome (webkit) , jest to jedyny sposób, w jaki udało mi się go uruchomić po wielu badaniach i testach.

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

Że będzie pracować dla pobierania następujący węzeł: <prefix:name>. Jak widać, przedrostek lub przestrzeń nazw są pomijane i będą dopasowywać elementy z różnymi przestrzeniami nazw pod warunkiem, że nazwa znacznika toname . Ale miejmy nadzieję, że nie będzie to dla ciebie problemem.

Nic z tego nie zadziałało (rozwijam rozszerzenie Google Chrome):

getElementsByTagNameNS("prefix", "name")

getElementsByTagName("prefix:name")

getElementsByTagName("prefix\\:name")

getElementsByTagName("name")

Edycja : po pewnym śnie znalazłem działające obejście :) Ta funkcja zwraca pierwszy węzeł pasujący do pełnego, nanodeNameprzykład<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;
}

Można go łatwo zmodyfikować w przypadku konieczności zwrócenia wszystkich pasujących elementów. Mam nadzieję, że to pomoże!

cprcrack
źródło
14

Żadne z powyższych rozwiązań nie działa tak dobrze. Znalazłem to i poprawiono szybkość. po prostu dodaj to, działało jak urok:

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

stosowanie:

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

źródło: http://www.steveworkman.com/html5-2/javascript/2011/improving-javascript-xml-node-finding-performance-by-2000/

Tj Tate
źródło
Dziękuję za fragment - jest to niezwykle pomocne / rozwiązuje problem.
Gilman,
3

Warto zauważyć, że od wersji jQuery 1.7 występowały problemy z niektórymi obejściami dotyczącymi znajdowania elementów w przestrzeni nazw. Zobacz te linki, aby uzyskać więcej informacji:

ArnisAndy
źródło
Jeśli wydajność jest ważna, najlepszym rozwiązaniem jest wybranie tagów bez jQuery. Dla porównania patrz: jsperf.com/node-vs-double-select/13
3

Znalezione rozwiązanie w komentarzu: Parsowanie XML z przestrzeniami nazw za pomocą jQuery $ (). Find

U mnie zadziałało używanie drugiej połowy nazwy węzła po dwukropku. Używane .find ( "lat") zamiast .find ( "geo \: lat") i pracował dla mnie.


Moja konfiguracja:

  • Chrome 42
  • jQuery 2.1.3

Przykładowy kod XML (fragment z interfejsu API Kontaktów Google):

<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 protected]" primary="true"/>
</entry>

Kod parsowania:

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

Mars Robertson
źródło
Cieszę się, że mogłem pomóc :)
Mike Grace
2

jQuery 1.7 nie działa z następującymi elementami:

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

Jednym z rozwiązań, które udało mi się pracować w Chrome, Firefox i IE, jest użycie selektorów, które działają w IE ORAZ selektory, które działają w Chrome, w oparciu o fakt, że jeden sposób działa w IE, a drugi w Chrome:

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

W IE zwraca to węzły przy użyciu przestrzeni nazw (Firefox i IE wymagają przestrzeni nazw), aw przeglądarce Chrome selektor zwraca węzły na podstawie selektora innego niż przestrzeń nazw. Nie testowałem tego w Safari, ale powinno działać, ponieważ działa w Chrome.

SeattleDiver
źródło
2

Moim rozwiązaniem (ponieważ używam proxy PHP) jest zastąpienie: przestrzeń nazw przez _ ... więc koniec problemów z przestrzenią nazw ;-)

Nie komplikuj !

Thomas Decaux
źródło
2

Oryginalna odpowiedź: jQuery XML parsing jak uzyskać atrybut elementu

Oto przykład, jak skutecznie uzyskać wartość w przeglądarce Chrome.

 item.description = jQuery(this).find("[nodeName=itunes\\:summary]").eq(0).text();
John Drefahl
źródło
2

Od początku 2016 r. Dla mnie z jQuery 1.12.0 działa następująca składnia:

  • IE 11 (11.0.9600.18204, aktualizacja 11.0.28, KB3134815): .find("z\\:row")
  • Firefox 44.0.2: .find("z\\:row")
  • Chrome 44.0.2403.89 m: .find("row")

Składnia .find("[nodeName=z:row]") nie działa w żadnej z wyżej wymienionych przeglądarek. Nie znalazłem sposobu na zastosowanie przestrzeni nazw w Chrome.

Podsumowując, następująca składnia działa we wszystkich wymienionych powyżej przeglądarkach: .find("row,z\\:row")

stefan.schwetschke
źródło
1

Jak wspomniano powyżej, istnieją problemy z powyższym rozwiązaniem w obecnych przeglądarkach / wersjach jQuery - sugerowana wtyczka nie działa całkowicie z powodu problemów z wielkością liter ( nodeNamejako właściwość czasami jest pisana wielkimi literami). Tak więc napisałem następującą szybką funkcję:

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

Przykładowe użycie:

$.findNS($(xml), 'x:row');
Mike Oliver
źródło
biorąc pod uwagę problemy z wersją jQuery, jest to zdecydowanie najlepsze rozwiązanie
MatteoSp
1

zadowolony: $this.find("content\\:encoded, encoded").text()

to idealne rozwiązanie ...

sachinkondana
źródło
1

Istnieje wtyczka jquery-xmlns dla jQuery do pracy z przestrzeniami nazw w selektorach.

Dima Fomin
źródło
0

Nie widziałem żadnej dokumentacji dotyczącej używania JQuery do analizowania XML. JQuery zwykle używa domeny przeglądarki do przeglądania dokumentu HTML, nie sądzę, aby czyta ona sam html.

Prawdopodobnie powinieneś spojrzeć na wbudowaną obsługę XML w samym JavaScript.

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

Chris Brandsma
źródło
3
Całkowicie się nie zgadzam. jQuery sprawia, że ​​obsługa XML odpowiedzi jest łatwa, jedyną komplikacją, jaką napotkasz, jest używanie przestrzeni nazw xml.
Richard Clayton
1
@Richard: Podczas korzystania z Ajax, jQuery używa responseXMLwłaściwości wbudowanego XMLHttpRequestobiektu, który w rzeczywistości jest dokumentem XML. Jednak jQuery (do 1.5, kiedy parseXMLzostał wprowadzony) nie miał możliwości analizowania XML, więc Chris miał rację.
Tim Down
0

właśnie zastąpiłem przestrzeń nazw pustym ciągiem. U mnie działa dobrze. Testowane rozwiązanie w różnych przeglądarkach: Firefox, IE, Chrome

Moim zadaniem było odczytanie i przeanalizowanie pliku EXCEL przez Sharepoint EXCEL REST API. Odpowiedź XML zawiera tagi z przestrzenią nazw „x:”.

Postanowiłem zastąpić przestrzeń nazw w XML-u pustym ciągiem. Działa w ten sposób: 1. Pobierz żądany węzeł z odpowiedzi XML 2. Przekształć wybrany węzeł Odpowiedź XML (dokument) na łańcuch 2. Zastąp przestrzeń nazw pustym ciągiem 3. Przekształć ciąg z powrotem na dokument XML

Zobacz zarys kodu tutaj ->

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
}

Rozwiązanie dotyczące konwersji XML na ciąg znaków znajdziesz tutaj: http://www.sencha.com/forum/showthread.php?34553-Convert-DOM-XML-Document-to-string

Karl
źródło
0

Alternatywnie możesz użyć fast-xml-parser w swoim projekcie i przekonwertować dane XML na obiekt JS / JSON. Następnie możesz użyć go jako właściwości obiektu. Nie używa JQuery ani innych bibliotek, ale rozwiąże Twój cel.

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>

Możesz ignorować przestrzenie nazw podczas analizowania do obiektu js / json. W takim przypadku możesz uzyskać bezpośredni dostęp do plików jsObj.xml.data.row.

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

Zastrzeżenie : stworzyłem parser fast-xml.

Amit Kumar Gupta
źródło
-1

W przypadku przeglądarek Webkit możesz po prostu pominąć dwukropek. Aby znaleźć <media:content>na przykład kanał RSS, możesz zrobić to:

$(this).find("content");
donnapep
źródło
W najnowszym safari nie obsługuje użycia. działa tylko w poprzedniej wersji.
Baryon Lee