Tratar XML con JavaScript Core

23 03 2009

Recientemente preguntaron en el maillist de javascript en español “ JavaEScript” sobre los ejemplos de W3Schools. El problema es que se quería insertar en una array el title, author, year y price de cada elemento book para posteriormente ordenarlos en una tabla. Al parecer estaban teniendo problema con que al leer el fichero no sabían discriminar los nodos que tiene. Los ejemplos de W3Schools utilizan la implementación DOM que viene en javascript por defecto en (casi) cualquier navegador moderno, el cuál no es precisamente fácil de utilizar y/o entender.
El XML en cuestión es el siguiente:

<bookstore><book category="cooking"> 
    <title lang="en">Everyday Italian</title> 
    <author>Giada De Laurentiis</author> 
    <year>2005</year> 
    <price>30.00</price> 
  </book> 
  <book category="children"> 
    <title lang="en">Harry Potter</title> 
    <author>J K. Rowling</author> 
    <year>2005</year> 
    <price>29.99</price> 
  </book> 
  <book category="web"> 
    <title lang="en">XQuery Kick Start</title> 
    <author>James McGovern</author> 
    <author>Per Bothner</author> 
    <author>Kurt Cagle</author> 
    <author>James Linn</author> 
    <author>Vaidyanathan Nagarajan</author> 
    <year>2003</year> 
    <price>49.99</price> 
  </book> 
  <book category="web" cover="paperback"> 
    <title lang="en">Learning XML</title> 
    <author>Erik T. Ray</author> 
    <year>2003</year> 
    <price>39.95</price> 
  </book> 
</bookstore>

Modifique el ejemplo de http://www.w3schools.com/dom/tryit.asp?filename=try_dom_list_loop, para utilizar el DOM core sin ninguna librería que facilitara el trabajo y a la vez usar código que entendiera la persona que hizo la pregunta, para hacer lo que se pedía:

<html>
 <head>
  <script type="text/javascript" src="loadxmldoc.js"></script>
 </head>
 <body>
 <script type="text/javascript">
   var arrayExterior = new Array();
   xmlDoc=loadXMLDoc("books.xml");
   x=xmlDoc.getElementsByTagName("book");
   for (i=0;i<x.length;i++)
   {
    var arrayInterior = new Array();
    for (j=0;j<x[i].childNodes.length;j++)
    {
    if (x[i].childNodes[j].nodeType==1)
    {
       var textValue = x[i].childNodes[j].childNodes[0].nodeValue;
       arrayInterior.push(textValue);
     }
   }
  arrayExterior.push(arrayInterior);
  }
 document.write("Total elementos: "+arrayExterior.length+"<br/>")
 for (i=0; i<arrayExterior.length; i++) {
  document.write("book "+i+"<br/>");
  for (j=0; j<arrayInterior.length; j++) {
    document.write(arrayExterior[i][j]+"<br/>");
  }
  document.write("<hr/>");
 }
</script>
</body>
</html>

El punto a destacar es que los nodos como book tienen un nodo implícito tipo text “invisible”, es decir, entre book y title existe un nodo text que no tienen ningún valor, algo similar a esto :

 <book category="children">
  <text-node/>
 <title lang="en">Harry Potter</title>

por eso lo mas fácil es un bucle que recorra los childNodes y preguntar el nodeType (1=Element, 3=Text, la lista completa aqui: http://www.w3schools.com/Dom/dom_nodetype.asp).