Convertir XML en JSON desde JavaScript

20 Abr 2011

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.

Más:

{11} Comentarios.

  1. jose

    Muy útil y práctico sin duda; mi enhorabuena de nuevo por este excelente recurso que nos aportas.

  2. yo mismo

    «#text»:» «,

    ¿esto no es un error? No veo de donde sale.

    • Carlos Benítez

      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!

  3. David

    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

    • Carlos Benítez

      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!

  4. David

    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!

    • Carlos Benítez

      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:

      results.ALEXA["@attributes"].VER // 0.9
      results.ALEXA.SD[0]["@attributes"].TITLE // 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!

  5. David

    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

  6. Alicia

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

    • Carlos Benítez

      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.

  7. Kinkaid

    Increíble, me soluciona la vida a mi que soy un torpe. Gracias

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *