=========================================== Recuperación de información =========================================== Introducción =========================================== En este tema veremos que podemos recuperar información de archivos XML *igual* que si fuesen bases de datos. Para ello podemos usar dos cosas: * Un lenguaje de programación de propósito general, como es Java. * O un lenguaje de programación especializado como es XQuery. En líneas generales hay dos grandes formas de usar un lenguaje de programación como Java para leer o escribir archivos XML. * DOM: significa Document Object Model (o Modelo del objeto documento). DOM en general almacena los archivos en memoria lo que es mucho más rápido y eficiente. * SAX: en algunos casos, los archivos muy grandes, pueden no caber en memoria. SAX proporciona otras clases y métodos distintos para ir procesando un archivo por partes. SAX significa Simple Access for XML, pero en general es un poco más complicado. En este módulo, no lo veremos. DOM es un estándar y sus clases y métodos existen en muchos otros lenguajes. XQuery ============= Para empezar **XQuery es un superconjunto de XPath** por lo que las expresiones básicas de XQuery también servirán en XQuery. La primera diferencia es que en XQuery tendremos que usar la función ``doc`` para cargar un archivo y luego navegar por él. Supongamos que tenemos un archivo de ejemplo como este: .. code-block:: xml Smith 20 Londres Jones 10 Paris Blake 30 Paris Clarke 20 Londres Adams 30 Atenas Tuerca Rojo 12 Londres Perno Verde 17 Paris Tornillo Azul 17 Roma Tornillo Rojo 14 Londres Leva Azul 12 Paris Engranaje Rojo 19 Londres Clasificador Paris Monitor Roma OCR Atenas Consola Atenas RAID Londres EDS Oslo Cinta Londres v1 p1 y1 200 v1 p1 y4 700 v2 p3 y1 400 v2 p3 y2 200 v2 p3 y3 300 v2 p3 y4 500 v2 p3 y5 600 v2 p3 y6 400 v2 p3 y7 600 v2 p5 y2 100 v3 p3 y1 200 v3 p4 y2 500 v4 p6 y3 300 v4 p6 y7 300 v5 p2 y2 200 v5 p2 y4 100 v5 p5 y5 500 v5 p6 y2 200 v5 p1 y4 100 v5 p3 y4 200 v5 p4 y4 800 v5 p5 y4 400 v5 p6 y4 500 En el esquema siguiente se muestra el mismo fichero en forma de tablas. .. figure:: graficos/BaseDatosProveedoresPartesProyectos.png :scale: 70% Estructura de tablas del XML de la base de datos de proveedores, partes y proyectos En él podríamos ejecutar consultas como estas: * Recuperar todos los proveedores con ``doc("datos.xml")/datos/proveedores`` * Recuperar todos los datos con ``doc("datos.xml")/datos/`` * Recuperar todas las partes con ``doc("datos.xml")/datos/partes``. De hecho, podemos usar las mismas consultas con predicados XPath y así por ejemplo extraer los datos del proveedor cuyo numprov es 'v1' con la consulta siguiente:: doc("datos.xml")/datos/proveedores/proveedor[@numprov='v1'] Que devuelve este resultado: .. code-block:: xml Smith 20 Londres Extraer los datos de partes cuyo color sea 'Rojo'. La consulta XQuery sería:: doc("datos.xml")/datos/partes/parte[color='Rojo'] Y el resultado sería: .. code-block:: xml Tuerca Rojo 12 Londres Tornillo Rojo 14 Londres Engranaje Rojo 19 Londres En XQuery se pueden mezclar las marcas con el programa. Sin embargo, para poder distinguir lo que se tiene que ejecutar de lo que no, tendremos que encerrar nuestras sentencias XQuery entre llaves y dejar las marcas fuera de las llaves. Así, esta consulta consigue generarnos un XML valido añadiendo un elemento raíz al conjunto de partes:: { doc("datos.xml")/datos/partes/parte[color='Rojo'] } El resultado devuelto es este: .. code-block:: xml Tuerca Rojo 12 Londres Tornillo Rojo 14 Londres Engranaje Rojo 19 Londres Antes se ha mencionado que se puede usar ``WHERE`` para crear condiciones. ¿Como cambiar entonces la consulta anterior para poner la condición en un ``WHERE`` y no meterla entre corchetes? Si queremos usar un ``where`` es porque queremos filtrar un conjunto de elementos, y si queremos un conjunto de elementos necesitaremos un bucle ``for``. Y a su vez, si recorremos un conjunto de elementos tendremos que hacer algún procesamiento con ellos o al menos devolverlos de la manera normal. Bucles ``for`` en XQuery --------------------------- Los bucles de XQuery son parecidos a los de Java. Hay una variable de bucle que iremos procesando de alguna manera. Dicha variable llevará siempre el simbolo del dolar ($). Así, un bucle que recupera todas las partes sería: .. code-block:: php for $p in doc("datos.xml")/datos/partes/parte return $p Si ahora queremos filtrar las partes rojas haremos esto: .. code-block:: php for $p in doc("datos.xml")/datos/partes/parte where $p/color='Rojo' return $p Y si ahora queremos un nuevo elemento raíz podremos hacer esto: .. code-block:: xml { for $p in doc("datos.xml")/datos/partes/parte where $p/color='Rojo' return $p } .. code-block:: xml { for $suministra in doc("datos.xml")/datos/suministros/suministra where $suministra/cantidad > 450 return $suministra } Ordenación en XQuery ------------------------- Si se desea ordenar un conjunto de datos puede usarse la clásula ``order by`` poniendo despues uno o varios elementos o atributos y usando ``ascending`` o ``descending``, de manera similar a SQL. Así, por ejemplo, para ordenar la consulta anterior por cantidad usaríamos esto: .. code-block:: xml { for $suministra in doc("datos.xml")/datos/suministros/suministra where $suministra/cantidad > 450 order by $suministra/cantidad descending return $suministra } Igual que en SQL se pueden combinar varios campos. Si por ejemplo quisiéramos ordenar por proveedor ascendente y luego por parte descendiente haríamos esto. .. code-block:: xml { for $suministra in doc("datos.xml")/datos/suministros/suministra where $suministra/cantidad > 450 order by $suministra/proveedor ascending, $suministra/parte descending return $suministra } Funciones en XQuery ---------------------- XQuery y XPath comparten funciones que permiten aplicar procesamiento extra a los nodos de un XML. A continuación se nombran algunas muy usadas: * ``concat($fila, ' ')`` concatena dos elementos, en este caso pone un espacio tras los datos de ``fila``. * ``string-length($elemento)`` devuelve la longitud de una cadena. * XQuery también tiene "funciones de agregación" al estilo de SQL como ``sum``, ``count`` y ``avg``. Además se pueden aplicar directamente a un conjunto sin necesidad de hacer un bucle ``for``. * En XQuery se usa ``distinct-values`` en lugar de ``distinct`` como hace SQL. Consultas de ejemplo ---------------------- Las siguientes consultas van referidas a la base de datos de proveedores y partes que aparecen al principio. Suministros grandes ~~~~~~~~~~~~~~~~~~~~~~ Usando ``where`` recuperar de la tabla de suministros todas las cantidades que sean mayores de 450. Encerrar los resultados dentro de un elemento raíz ``suministrosgrandes`` .. code-block:: xml { for $fila in doc("datos.xml")//suministra where $fila/cantidad > 450 return $fila/cantidad } Renombrado de etiquetas ~~~~~~~~~~~~~~~~~~~~~~~~ Usando ``where`` recuperar de la tabla de suministros todas las cantidades que sean mayores de 450 *y haciendo que las etiquetas cantidad pasen a llamarse numpartes* . Encerrar los resultados dentro de un elemento raíz ``suministrosgrandes`` .. code-block:: xml { for $fila in doc("datos.xml")//suministra where $fila/cantidad > 450 return {$fila/cantidad/text()} } Cruces o joins ~~~~~~~~~~~~~~~~~~ Recuperar la ciudad de los proveedores que suministran mas de 450 partes. Devolverlo todo dentro de un elemento raíz ``resultados`` haciendo que en cada fila haya un elemento ``datos`` que tenga a su vez tres hijos: * Un elemento ``numprov`` donde se vea el numero de proveedor. * Un elemento ``nombre`` donde se vea el nombre del proveedor. * Un elemento ``cantidadpartes`` donde vaya la cantidad de partes suministradas (que evidentemente debería ser siempre mayor de 450) .. code-block:: xml { for $proveedor in doc("datos.xml")/datos/proveedores/proveedor for $suministra in doc("datos.xml")/datos/suministros/suministra where $proveedor/@numprov=$suministra/numprov and $suministra/cantidad > 450 return {string($proveedor/@numprov)} {$proveedor/nombreprov/text()} {$suministra/cantidad/text()} } Fundamentos de DOM con Java =========================================== En primer lugar va a ser necesario importar las clases correctas para poder usar DOM. La línea correcta es .. code-block:: java import javax.xml.parsers.*; import org.w3c.dom.*; Un parser es un programa que analiza la sintaxis de un fichero, en nuestro caso un fichero XML. En castellano se debería decir analizador o analizador gramatical. Ejemplo de base =========================================== .. code-block:: java package com.ies; import javax.xml.parsers.*; import java.io.File; import org.w3c.dom.*; public class ProcesadorXML { public void procesarArchivo(String nombreArchivo){ DocumentBuilderFactory fabrica; DocumentBuilder constructor; Document documentoXML; File fichero=new File(nombreArchivo); fabrica= DocumentBuilderFactory.newInstance(); System.out.println("Procesando "+nombreArchivo); try { constructor= fabrica.newDocumentBuilder(); documentoXML=constructor.parse(fichero); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main (String[] argumentos){ System.out.println("Probando..."); ProcesadorXML proc=new ProcesadorXML(); proc.procesarArchivo("bolsas.xml"); } } La clase Document =========================================== La clase Document es una representación Java que almacena en memoria un archivo XML.Mediante esta clase y otras clases compañeras podremos recorrer cualquier punto del archivo XML. Este recorrido se basa siempre en la visita de nodos hijo o nodos hermano. No todos los nodos son iguales y se debe tener presente que en un nodo podríamos encontrar que los saltos de línea pueden ser un problema a la hora de recorrer el árbol DOM. Por ejemplo, dado un documento, se debe empezar obteniendo la raíz. Este elemento se llama también el “elemento documento” y podemos obtenerlo así: .. code-block:: java documento=constructor.parse(archivoXML); Element raiz=documento.getDocumentElement(); System.out.println(raiz.getNodeName()); La clase principal que nos interesa es la clase ``Node``, siendo ``Document`` su clase Hija. Algunos métodos de interés son estos: * ``getDocumentElement()`` obtiene el elemento raíz, a partir del cual podremos empezar a "navegar" a través de los elementos. * ``getFirstChild()`` obtiene el primer elemento hijo del nodo que estemos visitando. * ``getParentNode()`` nos permite obtener el nodo padre de un cierto nodo. * ``getChildNodes()`` obtiene todos los nodos hijo. * ``getNextSibling()`` obtiene el siguiente nodo hermano. * ``getChildNodes()`` devuelve un ``NodeList`` con todos los hijos de un elemento. Esta ``NodeList`` se puede recorrer con un ``for``, obteniendo el tamaño de la lista con ``getLength()`` y extrayendo los elementos con el método ``item(posicion)`` * ``getNodeType()`` es un método que nos indica el tipo de nodo (devuelve un ``short``). Podemos comparar con ``Node.ELEMENT_NODE`` para ver si el nodo es realmente un elemento. * Otro método de utilidad es ``getElementsByTagName`` que extrae todos los subelementos que tengan un cierto nombre de etiqueta. .. HINT:: En general, hay muchas clases que proporcionan más métodos de utilidad, como por ejemplo la clase ``Element``. En muchas ocasiones, podremos hacer un ``cast`` y aprovecharnos de ellos. Cuando se procesan archivos, se debe tener especial importancia a los espacios en blanco que pueda haber. Estos dos archivos no son iguales: .. code-block:: xml El primer hijo de listado es ... .. code-block:: xml Aquí el primer hijo de listado es \n ... Ejercicios =========================================== Ejercicio ------------------------------------------------------ Dado el siguiente archivo XML crear un programa que muestre todos los nombres: .. code-block:: xml Pepe Perez Empleado Juan Sanchez Gerente La solución podría ser algo así: .. code-block:: java public class ProcesadorXML { String ruta; public ProcesadorXML(String ruta){ this.ruta=ruta; } public Element getRaiz() throws ParserConfigurationException, SAXException, IOException{ DocumentBuilderFactory fabrica; fabrica= DocumentBuilderFactory.newInstance(); /* A partir de un fichero XML * crea el objeto documento en memoria*/ DocumentBuilder creadorObjDocumento; creadorObjDocumento= fabrica.newDocumentBuilder(); FileInputStream fich; fich=new FileInputStream(this.ruta); /* Analiza el XML y * lo carga en memoria */ Document documento; documento= creadorObjDocumento.parse(fich); Element raiz; raiz=documento.getDocumentElement(); return raiz; } /* Este método imprime todos los nombres*/ public void todosNombres() throws ParserConfigurationException, SAXException, IOException { Element raiz=getRaiz(); Node hijo=raiz.getFirstChild(); while (hijo!=null){ String nombreElemento; nombreElemento=hijo.getNodeName(); if (nombreElemento.equals("empleado")){ Node hijoFinLinea=hijo.getFirstChild(); Element hijoNombre=(Element) hijoFinLinea.getNextSibling(); String contenido=hijoNombre.getTextContent(); System.out.println("Empleado "+contenido); } hijo=hijo.getNextSibling(); } } public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { ProcesadorXML procesador; procesador=new ProcesadorXML( "D:/oscar/empleados.xml"); procesador.todosNombres(); } } Ampliaciones: * Añadir uno que devuelva todas las edades. * Añadir uno que devuelva los nombres de los empleados mayores de 30 (mostrando los nombres pero no las edades). * Añadir un método que diga cuantos empleados hay. El método debe ser capaz de tolerar que haya muchas líneas en blanco seguidas. El siguiente código resuelve el problema de mostrar los mayores de cierta edad: .. code-block:: java public void mostrarMayoresDe(int edadMinima) throws ParserConfigurationException, SAXException, IOException { Element raiz=getRaiz(); Node finLinea=raiz.getFirstChild(); Element empleado=(Element) finLinea.getNextSibling(); while (empleado!=null){ String edad=empleado.getAttribute("edad"); int iEdad=Integer.parseInt(edad); if (iEdad>edadMinima){ finLinea=empleado.getFirstChild(); Element elemNombre=(Element) finLinea.getNextSibling(); String nombreEmpleado= elemNombre.getTextContent(); System.out.println(nombreEmpleado + " es mayor de "+iEdad); } //Fin del if finLinea=empleado.getNextSibling(); empleado=(Element) finLinea.getNextSibling(); } //Fin del while } //Fin del método El método ``getElementsByTagName`` puede facilitar mucho el resolver ciertas tareas. Por ejemplo, supongamos que queremos resolver el problema de contar cuantos empleados hay: .. code-block:: java public int contarEmpleados() throws ParserConfigurationException, SAXException, IOException{ int numEmpleados=0; Element raiz=getRaiz(); NodeList lista=raiz.getElementsByTagName("empleado"); numEmpleados=lista.getLength(); return numEmpleados; } Ejercicio ---------------------------------------------- Extraer la raíz de un archivo XML .. code-block:: java public Node extraerRaiz(String nombreArchivo){ DocumentBuilderFactory fabrica; DocumentBuilder constructor; Document documentoXML=null; File fichero=new File(nombreArchivo); fabrica= DocumentBuilderFactory.newInstance(); System.out.println("Procesando "+nombreArchivo); try { constructor= fabrica.newDocumentBuilder(); documentoXML=constructor.parse(fichero); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return documentoXML.getDocumentElement(); } Ejercicio --------- Imprimir todos los elementos hijo del archivo XML. Una posibilidad es la siguiente: * Usar ``getNextSibling`` para ir recorriendo hermano a hermano hasta que se encuentre un ``null`` * Para evitar los nodos texto, solo imprimiremos cosas cuando el ``getNodeType()`` nos devuelva un tipo ``Node.ELEMENT_NODE`` .. code-block:: java public void imprimirHijos(Node nodoRaiz){ if (nodoRaiz==null){ System.out.println("Imposible procesar raiz null"); return ; } Node nodo=nodoRaiz.getFirstChild(); while (nodo!=null){ short tipo=nodo.getNodeType(); if (tipo==nodo.ELEMENT_NODE){ System.out.println("Nodo hijo:"+nodo.getNodeName()); } nodo=nodo.getNextSibling(); } } Otra posibilidad sería usar el ``getChildNodes`` que nos devuelve un vector con todos los hijos. Sin embargo ocurrirá lo mismo, deberemos evitar el visitar nodos texto, solo nos interesan los nodos Elemento. .. code-block:: java public void imprimirHijos2(Node nodoRaiz){ if (nodoRaiz==null){ System.out.println("Imposible procesar raíz nula"); return; } //Fin del if null NodeList lista=nodoRaiz.getChildNodes(); for (int i=0; i Cafe América Latina Libra esterlina 2.7:1 euros 1:0.87 dólares Islandia 9980 9950 10020 4.54% ...crear un programa XML que: * Busque todos los elementos cuya ciudad de procedencia sea "Madrid". * Si el elemento es un futuro mostrará el contenido de la etiqueta "producto". * Si el elemento es una divisa se mostrará el contenido de la etiqueta "nombre". * Si el elemento es una letra se mostrará el contenido de la etiqueta "pais_emisor". * Si el elemento es un bono se mostrará el contenido de la etiqueta "pais_de_procedencia". Una posibilidad (incompleta) sería esta: .. code-block:: java public class ProcesadorFinanzas { public static Element getRaiz (String rutaFichero){ Element raiz=null; DocumentBuilderFactory fabrica; fabrica=DocumentBuilderFactory.newInstance(); DocumentBuilder constructor; try { constructor=fabrica.newDocumentBuilder(); FileInputStream fichero; fichero=new FileInputStream(rutaFichero); Document documento; documento=constructor.parse(fichero); raiz=documento.getDocumentElement(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block } catch (IOException e) { e.printStackTrace(); } return raiz; } public static void mostrarMadrid(Element raiz){ NodeList hijos=raiz.getChildNodes(); for (int i=0; i``, que puede contener cualquier cosa (incluido Islandia) * La ciudad de procedencia no incluye la capital o ninguna ciudad de dicho país, así que podemos ignorar eso. * También aparece un elemento llamado ````, pero tampoco incluye Islandia, en principio también podemos saltarlo. Análisis del problema ~~~~~~~~~~~~~~~~~~~~~ Despues de haber examinado la DTD se llega a la conclusión de que el único elemento que puede transportar alguna clase de información relacionada con "Islandia" es el nodo ``país_de_procedencia``, que es un elemento hijo del elemento ``bono``. Solución ~~~~~~~~ * La clase ``Element`` tiene un método llamado ``getElementsByTagName`` que nos permite recuperar de una sola vez todos los elementos con el nombre ``bono``. * Se debe tener en cuenta que para llegar al elemento que nos interesa podemos seguir usando los métodos ``getFirstChild`` o ``getNextSibling`` para ir al primer hijo o para ir al siguiente hermano. * El contenido textual de un nodo se puede extraer con ``getTextContent`` * Al procesar un contenido textual, podríamos encontrar muchos espacios en blanco, tabuladores u otros elementos que alteren las comparaciones entre cadenas, por lo que deberemos usar métodos como ``trim()`` que limpian los espacios en blanco. .. code-block:: java public int algoQueVerCon(Node raiz, String nombrePais){ int cuantos=0; Element elementoRaiz=(Element) raiz; NodeList lista=elementoRaiz.getElementsByTagName("bono"); for (int i=0; ivalorMinimo) && (valorDeseadovalorMinimo) && (valorDeseado0){ //El producto sí es de Tokio String precio= hijo.getAttribute( "precio"); System.out.println("Precio:"+precio); } } Node finLinea=hijo.getNextSibling(); hijo=(Element) finLinea.getNextSibling(); } return vPrecios; } public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { ProcesadorXML procesador= new ProcesadorXML(); int[] precios= procesador.getPreciosInestables(); } } Solución 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. code-block:: java public String[] obtenerListaBonos(Node raiz){ int tamanioMaximo=1000; String[] ciudades=new String[tamanioMaximo]; String[] aux=new String[tamanioMaximo]; int posPrecio=0; Element eRaiz=(Element) raiz; NodeList listaBonos=eRaiz.getElementsByTagName("bono"); for (int i=0; i2){ return aux; } return ciudades; } Anexo =========================================== Código Java ---------------------------------------------- A continuación se muestra el código Java completo: .. code-block:: java package com.ies; import javax.xml.parsers.*; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import java.io.File; import org.w3c.dom.*; public class ProcesadorXML { public Node extraerRaiz(String nombreArchivo){ DocumentBuilderFactory fabrica; DocumentBuilder constructor; Document documentoXML=null; File fichero=new File(nombreArchivo); fabrica= DocumentBuilderFactory.newInstance(); System.out.println("Procesando "+nombreArchivo); try { constructor= fabrica.newDocumentBuilder(); documentoXML=constructor.parse(fichero); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return documentoXML.getDocumentElement(); } public void imprimirNombreDeLaRaiz(Node nodo){ if (nodo!=null){ String nombre=nodo.getNodeName(); System.out.println("La raíz se llama:"+nombre); Node primerHijo=nodo.getFirstChild(); String nombreHijo=primerHijo.getNodeName(); System.out.println("El primer hijo se llama <"+nombreHijo+">"); } else { System.out.println("No se pudo leer la raíz por ser nula"); } } public void imprimirHijos(Node nodoRaiz){ if (nodoRaiz==null){ System.out.println("Imposible procesar raiz null"); return ; } Node nodo=nodoRaiz.getFirstChild(); while (nodo!=null){ short tipo=nodo.getNodeType(); if (tipo==nodo.ELEMENT_NODE){ System.out.println("Nodo hijo:"+nodo.getNodeName()); } nodo=nodo.getNextSibling(); } } public void imprimirHijos2(Node nodoRaiz){ if (nodoRaiz==null){ System.out.println("Imposible procesar raíz nula"); return; } //Fin del if null NodeList lista=nodoRaiz.getChildNodes(); for (int i=0; ivalorMinimo) && (valorDeseado2){ return aux; } return ciudades; } public void crearElemento(Document d, String nombre,String contenido){ Element e=d.createElement(nombre); e.setTextContent(contenido); } public void crearRSS(){ DocumentBuilderFactory fabrica; DocumentBuilder constructor; Document documentoXML; try{ fabrica= DocumentBuilderFactory.newInstance(); constructor=fabrica.newDocumentBuilder(); documentoXML=constructor.newDocument(); TransformerFactory fabricaConv = TransformerFactory.newInstance(); Transformer transformador = fabricaConv.newTransformer(); DOMSource origenDOM = new DOMSource(documentoXML); Element e=documentoXML.createElement("rss"); documentoXML.appendChild(e); StreamResult resultado= new StreamResult( new File("D:\\oscar\\archivo.rss")); transformador.transform(origenDOM, resultado); } catch (Exception e){ System.out.print("No se han podido crear los"); System.out.println(" objetos necesarios."); e.printStackTrace(); return ; } } public static void main (String[] argumentos){ System.out.println("Probando..."); ProcesadorXML proc=new ProcesadorXML(); Node nodoRaiz=proc.extraerRaiz("bolsas.xml"); // proc.imprimirNombreDeLaRaiz(nodoRaiz); // proc.imprimirHijos(nodoRaiz); // proc.imprimirHijos2(nodoRaiz); // int cuantosFuturos=proc.contadorElementos(nodoRaiz, "futuro"); // System.out.println("Hay "+cuantosFuturos); // proc.imprimirPrecioBonos(nodoRaiz); // int inestables=proc.cuantosInestables(nodoRaiz); // System.out.println("Inestables hay:"+inestables); // float preciosTotales=proc.sumarAtributosPrecio(nodoRaiz); // System.out.println("La suma total es:"+preciosTotales); int cuantos=proc.algoQueVerCon(nodoRaiz, "Islandia"); // System.out.println("Num paises Islandia:"+cuantos); // cuantos=proc.cuantosFuturosTienenCiudadProcedencia( // nodoRaiz, "madrid"); // System.out.println("Futuros de Madrid:"+cuantos); // cuantos=proc.letrasConPaisEmisor(nodoRaiz, "espania"); // System.out.println("Letras con espania:"+cuantos); cuantos=proc.algoQueVerConEspania(nodoRaiz); //System.out.println("Productos rel. con España:"+cuantos); //proc.imprimirBonos(nodoRaiz); proc.imprimirMismaCiudad(nodoRaiz); String[] resultados=proc.obtenerListaBonos(nodoRaiz); System.out.println("La ciudad 0 es:"+resultados[0]); proc.crearRSS(); } } Archivo XML ---------------------------------------------- .. code-block:: xml ]> Cafe América Latina Libra esterlina 2.7:1 euros 1:0.87 dólares España 9980 9950 10020 España 9980 9950 10020 España 9980 9950 10020 España 9980 9950 10020 4.54% Procesamiento con SAX ============================== Simple Api for XML (o SAX) es un conjunto de clases para procesar XML de una forma muchísimo más eficiente (pero también más incómoda). Consiste en un *parser* que va leyendo etiqueta por etiqueta. Cada vez que el parser encuentra una etiqueta nueva nos lo comunicará mediante un evento y tendremos que incluir código de gestión de eventos que decidan que hacer. La forma más sencilla de trabajar es hacer que nuestra clase herede de ``DefaultHandler`` y sobrecargar el código de los métodos ``startElement`` y ``endElement``. Cuando se procesa XML podemos encontrarnos con que se usen o no espacios de nombres. Si usamos espacios de nombres SAX nos devolverá un argumento pero si no los usamos SAX nos devolverá otro argumento. Observemos el siguiente código: .. code-block:: java public class ProcesadorSAX extends DefaultHandler{ @Override public void startElement( String ns, String nombreCuandoHayNS, String nombreCuandoNoHayNS, Attributes atributos) throws SAXException { System.out.println(nombreCuandoNoHayNS); } } En este código SAX avisará a nuestro método ``startElement`` (el nombre debe ser así), cada vez que encuentre una etiqueta. Como en nuestros documentos no estamos usando espacios de nombres nos interesa imprimir el tercer parámetro. Para hacer que Java procese un fichero mediante SAX usando nuestra clase como procesador de etiquetas haremos lo siguiente: .. code-block:: java public class ProcesadorSAX extends DefaultHandler{ @Override public void startElement( String ns, String nombreCuandoHayNS, String nombreCuandoNoHayNS, Attributes atributos) throws SAXException { System.out.println(nombreCuandoNoHayNS); } public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException { SAXParserFactory fabrica; fabrica=SAXParserFactory.newInstance(); SAXParser parser=fabrica.newSAXParser(); XMLReader lector=parser.getXMLReader(); lector.setContentHandler(new ProcesadorSAX()); lector.parse( "c:/users/ogomez/documents/finanzas.xml"); } } Ahora Java irá leyendo el fichero etiqueta por etiqueta y mostrándonos los nombres de todas. No importará que el fichero ocupe varios GB, ya que SAX no cargará el fichero completo en memoria. Ejercicio: encontrar producto ---------------------------------- Encontrar todos los productos cuyo nombre sea "Cafe" y su mercado "América Latina". .. code-block:: java public void characters( char[] letras, int ini, int longitud){ if ((mercadoEncontrado) && (cafeEncontrado)){ String cadena=new String(letras, ini, longitud); if (cadena.equals("América Latina")){ System.out.println("Encontrado Cafe de AL"); cafeEncontrado=false; mercadoEncontrado=false; productoEncontrado=false; futuroEncontrado=false; } } if ((productoEncontrado) && (futuroEncontrado)){ String cadena=new String(letras, ini, longitud); if (cadena.equals("Cafe")){ cafeEncontrado=true; } //Fin del if interno } //Fin del if externo } //Fin del método characters Ejercicio: precios ----------------------- Encontrar todos las divisas cuya ciudad de procedencia sea "Madrid". Ejercicios para preparar examen =========================================== 1. Indicar cuantas letras tienen un tipo de interés inferior al 3% e indicar para cada una de ellas el país emisor. 2. Comprobar si hay alguna divisa con nombre "Euro" cuyo precio sea mayor de 195. 3. Indicar todos los productos que tengan la misma ciudad de procedencia.