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