Cómo utilizar jQuery para el análisis de XML con espacios de nombres

82

Soy nuevo en jQuery y me gustaría analizar un documento XML.

Puedo analizar XML normal con los espacios de nombres predeterminados pero con XML como:

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

Todo lo que realmente quiero es el <z:row>.

Hasta ahora, he estado usando:

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

realmente sin suerte. ¿Algunas ideas?

Brian Liang
fuente
Omitir el prefijo del espacio de nombres funcionó para mí. Vea esta respuesta: stackoverflow.com/a/25089647/2539811
Vincil Bishop

Respuestas:

135

Lo tengo.

Resulta que requiere \\escapar del colon.

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

Como señaló Rich:

La mejor solución no requiere escapar y funciona en todos los navegadores "modernos":

.find("[nodeName=z:row]")
Brian Liang
fuente
2
$('[nodeName=rs:data]', xml).find('[nodeName=z:row]')- funciona con 1.3.2 en WebKit (donde el método de dos puntos de escape aparentemente no lo hace)
gnarf
2
esto parece haber dejado de funcionar en jQuery versión 1.4.4, lo que creo que significa que jQuery tiene un mejor soporte de espacio de nombres XML. Entonces, para estar seguro, esto funciona$('[nodeName=rs:data],data')
Josh Pearce
15
Ahora jQuery 1.7 está disponible y esta última solución ya no funciona. ¿Cuál es la nueva forma?
Gapipro
3
En jQuery 1.8.x ya no funciona. Debería lograrse con una solución alternativa de compatibilidad de pseudoclase personalizada, como se explica aquí .
Miere
5
Aunque esto responde a la pregunta del documento XML dado, me gustaría recordarle a la gente que los prefijos les gustan rs, dto no sson realmente los espacios de nombres. Los espacios de nombres son los URN en la parte superior del archivo. Los prefijos son solo alias elegidos por el autor del documento para que sea breve. Se podría crear el mismo documento, que coincida con los mismos espacios de nombres, con prefijos totalmente diferentes. Animo a todos a buscar API que comprendan los espacios de nombres en lugar de asumir prefijos en sus consultas. Por ejemplo, en la API DOM del navegador puede utilizar getElementByTagNameNS()y getAttributeNS().
sergiopereira
35

He pasado varias horas leyendo sobre complementos y todo tipo de soluciones sin suerte.

ArnisAndy publicó un enlace a una discusión de jQuery, donde se ofrece esta respuesta y puedo confirmar que esto funciona para mí en Chrome (v18.0), FireFox (v11.0), IE (v9.08) y Safari (v5.1.5 ) usando jQuery (v1.7.2).

Estoy tratando de raspar un feed de WordPress donde el contenido se llama <contenido: codificado> y esto es lo que funcionó para mí:

content: $this.find("content\\:encoded, encoded").text()
Fasani
fuente
3
Este fue el único que funcionó de manera confiable para mí usando la última jQuery (misma versión), ¡así que gracias!
Dominic K
2
Esto funcionó para mí mientras yo utilicé un .each()bucle de repetición de itemelementos: $('dc\\:creator, creator', this).text(). Sin embargo, no estoy seguro de por qué , creatorse necesitaba el extra y dc\\:creatorno solo funcionó.
Fillip Peyton
20

Si está utilizando jquery 1.5, tendrá que agregar comillas alrededor del valor del atributo del selector de nodo para que funcione:

.find('[nodeName="z:row"]')
s0laris
fuente
19

Aunque la respuesta anterior parece ser correcta, no funciona en los navegadores webkit (Safari, Chrome). Creo que una mejor solución sería:

.find("[nodeName=z:myRow, myRow]")    
Rico
fuente
5
esto parece haber dejado de funcionar en jQuery versión 1.4.4, lo que creo que significa que jQuery tiene un mejor soporte de espacio de nombres XML. Entonces, para estar seguro, esto funciona$('[nodeName=rs:data],data')
Josh Pearce
16

En caso de que alguien necesite hacer esto sin jQuery , solo con Javascript normal, y para Google Chrome (webkit) , esta es la única forma que encontré para que funcione después de mucha investigación y pruebas.

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

Eso va a trabajar para recuperar el nodo siguiente: <prefix:name>. Como puede ver, se omite el prefijo o el espacio de nombres, y coincidirá con elementos con diferentes espacios de nombres siempre que el nombre de la etiqueta sea name. Pero espero que esto no sea un problema para ti.

Nada de esto funcionó para mí (estoy desarrollando una extensión de Google Chrome):

getElementsByTagNameNS("prefix", "name")

getElementsByTagName("prefix:name")

getElementsByTagName("prefix\\:name")

getElementsByTagName("name")

Editar : después de dormir un poco, encontré una solución que funciona :) Esta función devuelve el primer nodo que coincide con un completonodeNamecomo<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;
}

Se puede modificar fácilmente en caso de que necesite devolver todos los elementos coincidentes. ¡Espero eso ayude!

cprcrack
fuente
14

Ninguna de las soluciones anteriores funciona tan bien. Encontré esto y se ha mejorado la velocidad. solo agrega esto, funcionó como un encanto:

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

uso:

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

fuente: http://www.steveworkman.com/html5-2/javascript/2011/improving-javascript-xml-node-finding-performance-by-2000/

Tj Tate
fuente
Gracias por el fragmento: esto es extremadamente útil / resuelve el problema.
Gilman
3

Vale la pena señalar que a partir de jQuery 1.7 hubo problemas con algunas de las soluciones para encontrar elementos con espacios de nombres. Consulte estos enlaces para obtener más información:

ArnisAndy
fuente
Si el rendimiento es importante, la mejor solución es seleccionar las etiquetas sin jQuery. Para una comparación, consulte: jsperf.com/node-vs-double-select/13
3

Solución encontrada en el comentario: analizar XML con espacios de nombres usando jQuery $ (). Find

Usar la segunda mitad del nombre del nodo después de los dos puntos funcionó para mí. Usé .find ("lat") en lugar de .find ("geo \: lat") y funcionó para mí.


Mi configuración:

  • Cromo 42
  • jQuery 2.1.3

XML de muestra (fragmento de la API de contactos de 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>

Código de análisis:

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
fuente
Me alegro de haber podido ayudar :)
Mike Grace
2

jQuery 1.7 no funciona con lo siguiente:

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

Una solución que pude trabajar en Chrome, Firefox e IE es usar selectores que funcionan en IE Y selectores que funcionan en Chrome, basado en el hecho de que una forma funciona en IE y la otra en Chrome:

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

En IE, esto devuelve nodos que usan el espacio de nombres (Firefox e IE requieren el espacio de nombres), y en Chrome, el selector devuelve nodos basados ​​en el selector que no es de espacios de nombres. No lo he probado en Safari, pero debería funcionar porque funciona en Chrome.

SeattleDiver
fuente
2

Mi solución (porque uso un proxy Php) es reemplazar: espacio de nombres por _ ... así que no más problemas de espacio de nombres ;-)

Mantenlo simple !

Thomas Decaux
fuente
2

A principios de 2016, para mí, la siguiente sintaxis funciona con jQuery 1.12.0:

  • IE 11 (11.0.9600.18204, actualización 11.0.28, KB3134815): .find("z\\:row")
  • Firefox 44.0.2: .find("z\\:row")
  • Cromo 44.0.2403.89m: .find("row")

La sintaxis .find("[nodeName=z:row]")no funciona en ninguno de los navegadores mencionados anteriormente. No encontré forma de aplicar un espacio de nombres en Chrome.

Poniéndolo todo junto, la siguiente sintaxis funciona en todos los navegadores mencionados anteriormente: .find("row,z\\:row")

stefan.schwetschke
fuente
1

Como se mencionó anteriormente, hay problemas con la solución anterior con los navegadores / versiones actuales de jQuery: el complemento sugerido tampoco funciona completamente debido a problemas de mayúsculas y minúsculas ( nodeName, como propiedad, a veces está en mayúsculas). Entonces, escribí la siguiente función rápida:

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

Uso de ejemplo:

$.findNS($(xml), 'x:row');
Mike Oliver
fuente
dados los problemas de la versión de jQuery, esta es claramente la mejor solución
MatteoSp
1

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

es la solución perfecta ...

sachinkondana
fuente
1

Hay un complemento jquery-xmlns para que jQuery funcione con espacios de nombres en los selectores.

Dima Fomin
fuente
0

No he visto ninguna documentación sobre el uso de JQuery para analizar XML. JQuery generalmente usa el dom del navegador para navegar por un documento HTML, no creo que lea el html en sí.

Probablemente debería mirar el manejo XML integrado en JavaScript.

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

Chris Brandsma
fuente
3
Totalmente en desacuerdo. jQuery facilita el manejo de XML de respuesta, la única complicación que encontrará es el uso de espacios de nombres xml.
Richard Clayton
1
@Richard: Cuando se usa Ajax, jQuery usa la responseXMLpropiedad del XMLHttpRequestobjeto integrado , que de hecho es un documento XML. Sin embargo, jQuery (hasta la 1.5, cuando parseXMLse introdujo) no tenía forma de analizar XML, por lo que Chris tenía razón.
Tim Down
0

simplemente reemplazó el espacio de nombres por una cadena vacía. Funciona bien para mi. Solución probada en todos los navegadores: Firefox, IE, Chrome

Mi tarea era leer y analizar un archivo EXCEL a través de Sharepoint EXCEL REST API. La respuesta XML contiene etiquetas con espacio de nombres "x:".

Decidí reemplazar el espacio de nombres en el XML por una cadena vacía. Funciona de esta manera: 1. Saque el nodo de interés de la respuesta XML 2. Convierta el nodo XML-Response (Documento) en una cadena 2. Reemplace el espacio de nombres por una cadena vacía 3. Convierta la cadena de nuevo a un documento XML

Vea el esquema del código aquí ->

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
}

Para la conversión de XML a cadena, encuentre una solución aquí: http://www.sencha.com/forum/showthread.php?34553-Convert-DOM-XML-Document-to-string

Karl
fuente
0

Alternativamente, puede usar fast-xml-parser en su proyecto y convertir los datos XML en un objeto JS / JSON. Entonces puedes usarlo como propiedad de objeto. No usa JQuery u otras bibliotecas, pero resolverá su propósito.

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>

Puede ignorar los espacios de nombres mientras analiza el objeto js / json. En este caso puede acceder directamente como jsObj.xml.data.row.

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

Descargo de responsabilidad : he creado fast-xml-parser.

Amit Kumar Gupta
fuente
-1

Para los navegadores Webkit, puede dejar los dos puntos. Entonces, para buscar <media:content>en una fuente RSS, por ejemplo, puede hacer esto:

$(this).find("content");
donnapep
fuente
En el último safari, no admite el uso. funciona solo la versión anterior.
Baryon Lee