Vía David Walsh, nos llega esta interesante función que permite tomar un documento XML y convertirlo en un objeto JSON nativo directamente desde Javascript.
Esto resulta especialmente útil cuando, por ejemplo, tenemos que trabajar con web services u otras fuentes que aún utilizan XML como modelo para el intercambio de datos.
El código, es el siguiente:
function xmlToJson( xml ) { // Create the return object var obj = {}; if ( xml.nodeType == 1 ) { // element // do attributes if ( xml.attributes.length > 0 ) { obj["@attributes"] = {}; for ( var j = 0; j < xml.attributes.length; j++ ) { var attribute = xml.attributes.item( j ); obj["@attributes"][attribute.nodeName] = attribute.nodeValue; } } } else if ( xml.nodeType == 3 ) { // text obj = xml.nodeValue; } // do children if ( xml.hasChildNodes() ) { for( var i = 0; i < xml.childNodes.length; i++ ) { var item = xml.childNodes.item(i); var nodeName = item.nodeName; if ( typeof(obj[nodeName] ) == "undefined" ) { obj[nodeName] = xmlToJson( item ); } else { if ( typeof( obj[nodeName].push ) == "undefined" ) { var old = obj[nodeName]; obj[nodeName] = []; obj[nodeName].push( old ); } obj[nodeName].push( xmlToJson( item ) ); } } } return obj; }; |
Esta función recorre la estructura del documento almacenando sus nodos en un objeto de forma recursiva.
Ejemplo
Un documento XML con la siguiente estructura:
<ALEXA VER="0.9" URL="wikipedia.org/" HOME="0" AID="="> <SD TITLE="A" FLAGS="DMOZ" HOST="wikipedia.org"> <TITLE TEXT="Wikipedia"/> <ADDR STREET=" P.O. Box 78350" CITY=" San Francisco" STATE="CA" ZIP="94107" COUNTRY="US"/> <CREATED DATE="13-Jan-2001" DAY="13" MONTH="01" YEAR="2001"/> <PHONE NUMBER="+1-415-839-6885"/> <OWNER NAME="Wikimedia Foundation, Inc."/> <EMAIL ADDR="info-en@wikimedia.org"/> <DOS> <DO DOMAIN="wikiquote.org" TITLE="wikiquote.org"/> <DO DOMAIN="jimmywales.com" TITLE="jimmywales.com"/> </DOS> <LANG LEX="en"/> <LINKSIN NUM="431555"/> <SPEED TEXT="1242" PCT="58"/> <REVIEWS AVG="4.5" NUM="135"/> <CHILD SRATING="0"/> </SD> <KEYWORDS> <KEYWORD VAL="On the Web"/> <KEYWORD VAL="Tracking"/> </KEYWORDS> <SD> <POPULARITY URL="wikipedia.org/" TEXT="8"/> <REACH RANK="5"/> <RANK DELTA="+0"/> </SD> </ALEXA> |
se convierte en un objeto válido JSON como este:
{ "ALEXA":{ "@attributes":{ "VER":"0.9", "URL":"wikipedia.org/", "HOME":"0", "AID":"=" }, "SD":[ { "@attributes":{ "TITLE":"A", "FLAGS":"DMOZ", "HOST":"wikipedia.org" }, "TITLE":{ "@attributes":{ "TEXT":"Wikipedia" } }, "ADDR":{ "@attributes":{ "STREET":" P.O. Box 78350", "CITY":" San Francisco", "STATE":"CA", "IP":"94107", "COUNTRY":"US" } }, "CREATED":{ "@attributes":{ "DATE":"13-Jan-2001", "DAY":"13", "MONTH":"01", "YEAR":"2001" } }, "PHONE":{ "@attributes":{ "NUMBER":"+1-415-839-6885" } }, "#text":" ", "OWNER":{ "@attributes":{ "NAME":"Wikimedia Foundation, Inc." } }, "EMAIL":{ "@attributes":{ "ADDR":"info-en@wikimedia.org" } }, "DOS":{ "DO":[ { "@attributes":{ "DOMAIN":"wikiquote.org", "TITLE":"wikiquote.org" } }, { "@attributes":{ "DOMAIN":"jimmywales.com", "TITLE":"jimmywales.com" } } ] }, "LANG":{ "@attributes":{ "LEX":"en" } }, "LINKSIN":{ "@attributes":{ "NUM":"431555" } }, "SPEED":{ "@attributes":{ "TEXT":"1242", "PCT":"58" } }, "REVIEWS":{ "@attributes":{ "AVG":"4.5", "NUM":"135" } }, "CHILD":{ "@attributes":{ "SRATING":"0" } } }, { "POPULARITY":{ "@attributes":{ "URL":"wikipedia.org/", "TEXT":"8" } }, "REACH":{ "@attributes":{ "RANK":"5" } }, "RANK":{ "@attributes":{ "DELTA":"+0" } } } ], "KEYWORDS":{ "KEYWORD":[ { "@attributes":{ "VAL":"On the Web" } }, { "@attributes":{ "VAL":"Tracking" } } ] } } } |
El resultado, como podemos comprobar, es mucho más manejable de este modo al permitirnos trabajar con estructuras muy anidadas de una forma nativa en Javascript.
El acceso a cada uno de los nodos sería ahora posible mediante una notación simple JSON.
Sin duda, se trata de un recurso muy útil que deberíamos tener a mano para facilitar el intercambio de datos entre aplicaciones.
Muy útil y práctico sin duda; mi enhorabuena de nuevo por este excelente recurso que nos aportas.
«#text»:» «,
¿esto no es un error? No veo de donde sale.
No es un error; es un espacio en blanco dentro del documento XML.
Podríamos arreglarlo modificando ligeramente la función pero puede resultar interesante el mantener la correlación.
Saludos!
Hola,
Por pura ignorancia, ¿Qué significa la arroba que sale antes de attributes? ¿Por qué se utiliza esta notación, tiene alguna ventaja?
Muchas gracias y un saludo
La arroba es una simple conveción. No tiene mayores ventajas que la de identificar rápidamente un determinado elemento dentro de una nomenclatura, en este caso, un atributo que a su vez, es un objeto.
Saludos!
Muchísimas gracias por tu rápida respuesta. Creo que me ha quedado claro. Mi pregunta ahora es la siguiente, como se harían las llamadas al objeto? siguiendo con tu ejemplo:
version = obj.ALEXA.attributes.VER
ó
version = obj.ALEXA.VER?
o ninguna de las 2 y hay que poner la @?
Graciaaas!
Pues depende de dónde hayas guardado el objeto.
Suponiendo que hemos creado una variable ‘result’ que almacena el JSON, para acceder tendríamos que usar una sintaxis similar a:
Como ves, para acceder a los campos con arroba, es necesario el recurrir a los corchetes.
Tienes más información sobre lo ‘estándar’ de la arroba en este hilo de StackOverflow:
http://stackoverflow.com/questions/5957371/is-use-of-attributes-in-json-non-standard-or-standard
Saludos!
Muchas gracias de nuevo Carlos. Por lo que veo hace un poco más difícil la notación por puntos, pero es un poco más claro quizá.
Saludos
Como cargas el archivo xml ???
He probado a almacenarlo directamente en una variable llamanda ‘xml’ y luego llamar a la función pasándole esta como atributo y almacenar el resultado en una variable consultandola con console.log() pero no obtengo ningún resultado….
Hola;
el archivo XML tiene que llegar via servidor; no vale el cargarlo en una variable Javascript porque entonces pasa de ser un objeto XML a una simple cadena (String).
Lo habitual es que este formato nos llegue tras una llamada AJAX como las que podemos realizar, por ejemplo, con jQuery.ajax().
Hay que tener en cuenta que lo interesante de esta función es poder manejar los datos que nos devuelve un determinado API, de ahí que lo natural sería obtener dichos datos tras una llamada del tipo anterior.
No obstante, también es posible hacerlo desde una variable como comentas, pero entonces es necesario un paso intermedio de ‘parseo’. Esa funcionalidad podemos conseguirla a través también de jQuery gracias al método jQuery.parseXML()
Saludos.
Increíble, me soluciona la vida a mi que soy un torpe. Gracias