Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Manejo de Ficheros - AC

Descargar como pdf o txt
Descargar como pdf o txt
Está en la página 1de 21

RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma

MÓDULO Acceso a datos CURSO: 2º


PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

Tema 1: Manejo de Ficheros

Índice
1. Manejo de ficheros ................................................................................................................................ 1
1.1. Accesos a ficheros .......................................................................................................................... 1
1.2. Gestión de flujos de datos .............................................................................................................. 2
1.2.1. Ficheros de caracteres ............................................................................................................ 2
1.2.2. Ficheros binarios..................................................................................................................... 4
1.3. Objetos serializables ....................................................................................................................... 7
2. Trabajo con ficheros XML ....................................................................................................................... 9
2.1. Acceso a datos con DOM ................................................................................................................ 9
2.1.1. DOM y Java ........................................................................................................................... 11
2.1.2. Generar árbol DOM .............................................................................................................. 12
2.1.3. Recorrer un árbol DOM......................................................................................................... 13
2.1.4. Modificar y serializar un árbol DOM ...................................................................................... 15
2.2. SAX............................................................................................................................................... 16
2.2.1. Abrir XML con SAX desde Java .............................................................................................. 17
2.2.2. Recorrer XML con SAX .......................................................................................................... 17
2.3. Crear un documento XML con StAX .............................................................................................. 20
RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

1. Manejo de ficheros
Una de las maneras más sencillas en Java de conseguir la persistencia es usando ficheros. Veremos a
continuación algunas alternativas que existen en Java.

1.1. Accesos a ficheros


En Java se dispone del paquete java.io para trabajar con ficheros. Se pueden clasificar según dos
criterios:
 Según el tipo de contenido:
 Ficheros de caracteres: son aquellos compuestos exclusivamente con caracteres, por lo que
pueden ser creados y visualizados utilizando cualquier editor de texto que ofrezca el sistema
operativo (por ejemplo: Notepad, Vi, etc.).
 Ficheros binarios: son aquellos que están compuestos por bytes.
 Según el modo de acceso:
 Ficheros secuenciales: en este tipo de ficheros la información es almacenada como una
secuencia de bytes (o caracteres), de manera que para acceder al byte (o carácter) i-ésimo,
es necesario pasar antes por todos los anteriores (i-1).
 Ficheros aleatorios: a diferencia de los anteriores el acceso puede ser directamente a una
posición concreta del fichero pudiéndose recuperar la información en cualquier orden.
Con independencia del modo, Java define la clase File1 que representa un archivo o un directorio dentro
de un sistema de ficheros.
Un objeto de la clase File representa el nombre de un fichero o de un directorio y sus métodos permiten
obtener toda la información sobre las características de los mismos. Un objeto File puede, entre otros,
usar los siguientes constructores:
File f = new File (String directorio+fichero)
File f = new File (String directorio, String nombrefichero)
File f = new File (File directorio, String fichero)

Algunos métodos interesante del objeto File son:


 getName(): devuelve el nombre del fichero/directorio.
 getPath(): devuelve el camino relativo.
 getAbsolutePath(): devuelve el camino absoluto del fichero/directorio.
 canRead(): devuelve true si el fichero se puede leer.
 canWrite(): devuelve true si el fichero se puede escribir.
 length(): nos devuelve el tamaño del fichero en bytes.
 createNewFile(): crea un nuevo fichero, vacío, asociado a File si y solo si no existe un fichero con
dicho nombre.
 delete(): borra el fichero o directorio asociado al File.
 exists(): devuelve true si el fichero/directorio existe.
 getParent(): devuelve el nombre del directorio padre, o null si no existe.

1
http:/docs.oracle.com/javase/8/docs/api/java/io/File.html

Tema 1: Manejo de ficheros 1


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

 isDirectory(): devuelve true si el objeto File corresponde a un directorio.


 isFile (): devuelve true si el objeto File corresponde a un fichero.
 listFiles(): devuelve un array de objetos de tipo file, con los ficheros que contiene el directorio.
 mkdir(): crea un directorio con el nombre indicado en la creación del objeto File.
 renameTo(File nuevoNombre): renombra el fichero.

Ejemplo 1
File f = new File ("d:\\textos\ejemplo.txt") ;

Si sobre ese nuevo objeto File creado se aplica el siguiente código:


System.out.println ("Nombre :" + f.getName());
System.out.println ("Directorio padre :" + f.getParent());
System.out.println ("Ruta absoluta :" + f.getAbsolutePath());

El resultado será:
Nombre: ejemplo.txt
Directorio padre: d:\textos\
Ruta absoluta: d:\textos\ejemplo.txt

Actividad 1
Muestra, por separado, la lista de ficheros y directorios de un directorio dado.

1.2. Gestión de flujos de datos 2


En Java, el acceso a ficheros es tratado como un flujo (stream) de información entre el programa y el
fichero. Un flujo no es más que un objeto que hace de intermediario entre el programa y el origen o el
destino de la información
Los problemas de gestión del archivo, por ejemplo, cómo se escribe en binario o cómo se hace cuando el
acceso es secuencial o es aleatorio, quedan bajo la responsabilidad de la clase que implementa el flujo. El
programador se abstrae de esos inconvenientes y se dedica exclusivamente a trabajar con los datos que
lee y recupera.
1.2.1. Ficheros de caracteres
Las clases de flujos de caracteres más importantes son:
 Para leer y escribir caracteres en ficheros usamos FileReader y FileWriter.
Estas clases pueden generar las excepciones FileNotFoundException (nombre del fichero no
válido o este no existe) y lOException (el disco está lleno, está protegido contra escritura,…).
El flujo FileReader permite leer caracteres desde un fichero de manera secuencial.
Ejemplo 2
import java.io.*;
public class Ejemplo2 {
public static void main(String[] args) {
File f = new File("d:/alumnos.xml");
try {
FileReader fich = new FileReader(f); // crear flujo de entrada

2
Ver en http://docs.oracle.com/javase/8/docs/api/java/io/package-summary.html todas las posibilidades

Tema 1: Manejo de ficheros 2


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

int i ;
while ((i = fich.read()) != -1) // se lee un carácter
System.out.print((char) i);
fich.close(); // cerrar fichero
} catch (FileNotFoundException e) {
System.err.println("Fichero no encontrado");
} catch (IOException e) {
System.err.println("Error de E/S");
}
}
}

El método read devuelve el carácter leído como un entero o -1 si se ha llegado al final del
fichero.
En el ejemplo anterior si quisiésemos leer de 10 en 10 caracteres podemos usar un buffer:
char b[]= new char[10];
while ((i = fich.read(b)) != -1)
System.out.println(b);

En este caso el método read devuelve el numero de caracteres leídos o -1 si se ha llegado al final
del fichero.
Una vez se han leído o se han escrito todos los datos necesario se ha de cerrar el flujo para
liberar recursos.
El flujo FileWriter permite escribir caracteres en un fichero de modo secuencial. Si es el fichero
de destino existe se vacía por lo que si se desea añadir contenido al final del mismo sin eliminar
el contenido anterior se ha de usar el constructor:
FileWriter (String ruta, boolean añadir)

Donde el parámetro ruta indica la localización del archivo en el sistema operativo y el parámetro
añadir igual a true indica que se van añadir datos al final del fichero..
Ejemplo 3
import java.io.*;
public class Ejemplo3 {

public static void main(String[] args) {


File f = new File("d:\\alumnos.txt");
try {
FileWriter fich = new FileWriter(f,true); //crear flujo salida
fich.write("Lorem ipsum dolor sit amet");
fich.write(System.getProperty("line.separator"));
fich.close(); // cerrar fichero
} catch (IOException e) {
System.err.println("Error de E/S");
}
}
}

Tema 1: Manejo de ficheros 3


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

 Para leer y escribir cadenas de caracteres usamos scanner y PrintWriter.


Ejemplo 5:Leer cadenas de caracteres
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class Ejemplo5 {
public static void main(String[] args) {
File f = new File("d:\\alumnos2.txt");
try {
Scanner sc = new Scanner(f);
while (sc.hasNext())
System.out.println(sc.nextLine());
sc.close();
} catch (FileNotFoundException e) {
System.err.println("Fichero no encontrado");
}
}
}

Ejemplo 4: Escribir cadenas de caracteres


import java.io.*;
public class Ejemplo4 {
public static void main(String[] args) {
File f = new File("d:\\alumnos.txt");
try {
PrintWriter fich = new PrintWriter(f);
fich.println("Lorem ipsum dolor sit amet");
fich.close(); // cerrar fichero
} catch (FileNotFoundException e) {
System.err.println("Fichero no encontrado");
}
}
}

Si deseamos añadir al final del archivo deberemos pasarle un objeto de tipo FileWriter con el
parámetro append a true.
PrintWriter fich = new PrintWriter(new FileWriter(f,true));

1.2.2. Ficheros binarios


Los ficheros binarios almacenan secuencias de datos binarios que no son legibles directamente pero
tienen la ventaja de que ocupan menos espacio en disco.
Usaremos las dos clases siguientes para trabajar con ficheros binarios::
 FileOutputStream: permite escribir bytes en un fichero de manera secuencial.
 FilelnputStream: permite leer bytes en un fichero de manera secuencial. Los métodos que
proporciona son similares a los vistos para clase FileReader, estos métodos devuelven el byte
leído o el número de bytes leídos (según el read utilizado) o -1 si se ha llegado al final del fichero.

Tema 1: Manejo de ficheros 4


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

Ejemplo 6
import java.io.*;
public class Ejemplo6 {
public static void main(String[] args) throws IOException {
File fichero = new File("d:\\FichBytes.dat");
// crea flujo de salida hacia el fichero
FileOutputStream fileout = new FileOutputStream(fichero);
// crea flujo de entrada
FileInputStream filein = new FileInputStream(fichero);
int i;
for (i = 1; i < 100; i++)
fileout.write(i); // escribe datos en el flujo de salida
fileout.close(); // cerrar flujo salida

while ((i = filein.read()) != -1) // lee datos del flujo de entrada


System.out.print(i + " ");
filein.close(); // cerrar flujo de entrada
}
}

Para añadir bytes al final del fichero usaremos el siguiente constructor para FileOutputStream, colocando
en el segundo parámetro del constructor el valor true:
FileOutputStream fileout = new FileOutputStream (fichero, true);

Al igual que con los ficheros de caracteres no es obligatorio leer elemento a elemento sino que
podremos trabajar con un buffer.
Ejemplo 7: Descargar un fichero
FileOutputStream fout=new FileOutputStream("salida.mp3");
InputStream is= new URL("http://example.com/cancion.mp3").openStream();
int i;
byte[] buffer = new byte[1000];
while ((i = is.read(buffer)) != -1) {
fout.write(buffer, 0, i);
}
fout.close();
is.close();

Para leer y escribir datos de tipos primitivos: int, float, long, etc. usaremos las clases DatalnputStream y
DataOutputStream. Estas clases además de los métodos read() y write() vistos anteriormente
proporcionan métodos para la lectura y escritura de tipos. Algunos de los métodos se muestran en la
siguiente tabla:
MÉTODOS PARA LECTURA MÉTODOS PARA ESCRITURA
boolean readBoolean(); void writeBoolean(boolean v);
byte readByte(); void writeByte(int v);
short readShort(); void writeBytes(String s);
char readChar(); void writeChars(String s);
int readInt(); void writeChar(int v);
long readLong(); void writeInt(int v);
float readFloat(); void writeFloat(float v);
double readDouble(); void writeDouble(double v);
String readUTF(); void writeUTF(String str);

Tema 1: Manejo de ficheros 5


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

Para abrir un objeto DataInputStream usaremos:


File fichero = new File("d:\\FichBytes.dat");
FileInputStream filein = new FileInputStream (fichero);
DataInputStream dataIn = new DataInputStream (filein);

Para abrir un objeto DataOutputStream usaremos:


File fichero = new File("d:\\FichBytes.dat");
FileOutputStream fileout = new FileOutputStream (fichero) ;
DataOutputStream dataOut = new DataOutputStream(fileout);

El siguiente ejemplo inserta datos en el fichero FichData.dat, los datos los toma de dos arrays, uno
contiene los nombres de una serie de personas y el otro sus edades, recorremos los arrays y vamos
escribiendo su contenido en el fichero:
Ejemplo 7
import java.io.*;

public class Ejemplo7 {


public static void main(String[] args) throws IOException {
File fichero = new File("D:\\FichData.dat");
FileOutputStream fileout = new FileOutputStream(fichero);
DataOutputStream dataOS = new DataOutputStream(fileout);
String nombres[] = { "Ana", "Luis Miguel", "Alicia", "Pedro" };
int edades[] = { 14, 15, 13, 15 };
for (int i = 0; i < edades.length; i++) {
dataOS.writeUTF(nombres[i]); // inserta nombre
dataOS.writeInt(edades[i]); // inserta edad
}
dataOS.close(); // cerrar flujo
}
}

El siguiente ejemplo visualiza los datos grabados anteriormente en el fichero, se deben recuperar el
mismo orden en el que se insertaron, es decir, primero obtenemos el nombre y luego la edad:
Ejemplo 8
import java.io.*;
public class Ejemplo8 {
public static void main(String[] args) throws IOException {
File fichero = new File("D:\\FichData.dat");
FileInputStream filein = new FileInputStream(fichero);
DataInputStream datain = new DataInputStream(filein);
String n;
int e;
try {
while (true) {
n = datain.readUTF(); // recupera el nombre
e = datain.readInt(); // recupera la edad
System.out.println("Nombre: " + n + ", edad: " + e);
}
} catch (EOFException eo) { }
datain.close(); // cerrar flujo
}
}

Tema 1: Manejo de ficheros 6


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

Obteniéndose la siguiente salida:


Nombre: Ana, edad: 14
Nombre: Luis Miguel, edad: 15
Nombre: Alicia, edad: 13
Nombre: Pedro, edad: 15

1.3. Objetos serializables


Java nos permite leer y guardar objetos completos en ficheros binarios (sin tener que guardar cada uno
de sus atributos de forma separada) mediante las clases ObjectlnputStream y ObjectOutputStream.
Se necesita que la clase implemente la interfaz Serializable.
Ejemplo 9
import java.io.Serializable;
public class Persona implements Serializable {
private String nombre= null;
private int edad=-1;

public Persona() { }
public Persona(String nombre, int edad) {
this.nombre = nombre;
this.edad = edad;
}

public void setNombre(String nom) { nombre = nom; }


public void setEdad(int ed) { edad = ed;}
public String getNombre() { return nombre; }
public int getEdad() { return edad; }
}

El siguiente ejemplo escribe objetos Persona en un fichero. Necesitamos crear un flujo de salida a disco
con FileOutputStream y a continuación se vincula este con el flujo de salida ObjectOutputStream, que es
el que procesa los datos:
File fichero = new File ("D:\\FichPersona.dat");
FileOutputStream fileout = new FileOutputStream (fichero) ;
ObjectOutputStream dataOS = new ObjectOutputStream (fileout) ;

El método writeObject() escribe los objetos al flujo de salida y los guarda en un fichero en disco:
dataOS.writeObject(persona) ;

El código es el siguiente:
Ejemplo 9: Continuación
import java.io.*;
public class EscribePersona {
public static void main(String[] arg) {
Persona persona;
File fichero = new File("D:\\FichPersona.dat");
try {
FileOutputStream fileout = new FileOutputStream(fichero);
ObjectOutputStream dataOS = new ObjectOutputStream(fileout);
String nombres[] = { "Ana", "Luis Miguel", "Alicia", "Pedro" };
int edades[] = { 14, 15, 13, 15 };

Tema 1: Manejo de ficheros 7


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

for (int i = 0; i < edades.length; i++) {


persona = new Persona(nombres[i], edades[i]);
dataOS.writeObject(persona); // escribir una persona
}
dataOS.close();
} catch (IOException e) {
System.out.println("Error en el manejo del fichero");
}
}
}

Para leer objetos Persona del fichero necesitamos el flujo de entrada a disco FilelnputStream y a
continuación crear el flujo de entrada ObjectlnputStream que es el que procesa los datos y se ha de
vincular al fichero de FilelnputStream:
File fichero = new File ("d:/FichPersona.dat");
FileInputStream filein = new FileInputStream (fichero);
ObjectInputStream datain = new ObjectInputStream (filein);
El método readObject() lee los objetos del flujo de entrada, puede lanzar la excepción
ClassNotFoundException:
persona = (Persona) datain.readObject();
El código es el siguiente:
Ejemplo 9: Continuación
import java.io.*;
public class LeePersona {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Persona persona;
File fichero = new File("D:/FichPersona.dat");
if (fichero.exists()) {
FileInputStream fileIn = new FileInputStream(fichero);
ObjectInputStream dataIn = new ObjectInputStream(fileIn);
try {
while (true) { // lectura del fichero
persona = (Persona) dataIn.readObject(); // leer una Persona
System.out.println("Nombre: " + persona.getNombre()
+ ", edad: " + persona.getEdad());
}
} catch (EOFException e) {
dataIn.close();
}
} else System.out.println("El fichero no existe");
}
}

Tema 1: Manejo de ficheros 8


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

2. Trabajo con ficheros XML


Aunque los ficheros XML son ficheros de texto, y nada nos impediría trabajar con las clases vistas en el
punto anterior, existe una serie de herramientas, conocidas como analizadores sintácticos o parsers para
trabajar de forma específica con estos tipos de ficheros.
Una clasificación de las herramientas para el acceso a ficheros XML puede ser:
 Herramientas que no validan los documentos XML: Comprueban la validez sintáctica del
documento XML pero no lo validan respecto a un esquema asociado. Ejemplos de este tipo de
herramientas son DOM y SAX.
 Herramientas que validan los documentos XML. Comprueban la validez sintáctica del documento
XML y además lo validan respecto a un esquema asociado. Un ejemplo de este tipo de
herramientas es JAXB (Java Architecture for XML Binding).
A continuación se describe el acceso a datos con las tecnologías más extendidas: DOM y SAX.

2.1. Acceso a datos con DOM3


La tecnología DOM (Document Object Model) permite analizar y manipular dinámicamente un
documento XML. Tiene su origen en el Consorcio World Wide Web (W3C).
DOM crea un árbol en memoria con la estructura del fichero XML. Cada elementos del documento se
transforman de forma jerárquica en un nodo del árbol, estos nodos pueden ser de los siguientes tipos:
 Raíz: El nodo superior del árbol (obligatorio).
 Padre: Nodo con hijos.
 Hijo: Nodo descendiente de otro nodo.
 Hermanos: Nodos que comparten el mismo padre.
 Hojas: Nodos sin hijos.
Una vez creado el árbol, podremos, mediante los métodos proporcionados por DOM, navegar a través
de sus nodos.
El siguiente un documento XML representan películas de ciencia ficción. Cada película está definido por
un atributo fechaEstreno, un valor que indica el año de estreno de la película y por dos elementos hijo:
Título y director.
<Peliculas xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="EsquemaPeliculas.xsd">
<Pelicula fechaEstreno="1984">
<Titulo>Dune</Titulo>
<Director>David Lynch</Director>
</Pelicula>
<Pelicula fechaEstreno="1982">
<Titulo>Blade Runner</Titulo>
<Director>Ridley Scott</Director>
</Pelicula>
<Pelicula fechaEstreno="1979">
<Titulo>Alien: el octavo pasajero</Titulo>
<Director>Ridley Scott</Director>
</Pelicula>
</Peliculas>

3
https://es.wikipedia.org/wiki/Document_Object_Model

Tema 1: Manejo de ficheros 9


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

El árbol DOM asociado al documento XML es el siguiente, teniendo en cuenta que faltan por expandir los
nodos pertenecientes a dos películas.

La generación del árbol DOM a partir de un documento XML se hace de la siguiente forma:
1. DOM no valida el documento mediante el esquema asociado (aunque se puede cambiar este
comportamiento) y solo comprueba que el documento este bien formado
2. El primer paso es crear el nodo raíz película asociado con el elemento <Películas>.
3. De <Películas> cuelgan en el documento tres hijos <Película> de tipo elemento, por tanto el
árbol crea 3 nodos Película descendiente de Películas.
4. Cada elemento <Película> en el documento tiene asociado un atributo fechaExtreno. En DOM,
los atributos son listas con el nombre del atributo y el valor. La lista contiene tantas tuplas
(nombre, valor) como atributos haya en el elemento. En este caso solo hay un atributo en el
elemento <Película>.
5. Cada <Película> tiene dos elementos que descienden de él y que son <Titulo> y <Director>. Al ser
elementos, estos son representados en DOM como nodos descendientes de Película, al igual que
se ha hecho con Película al descender de Películas.
6. Cada elemento <Titulo> y <Director> tiene un valor que es de tipo cadena de texto. Los valores
de los elementos son representados en DOM como nodos #text. Sin duda esta es la parte más
importante del modelo DOM. Los valores de los elementos son nodos también, a los que
internamente DOM llama #text y que descienden del nodo que representa el elemento que
contiene ese valor.
7. Hay que tener en cuenta que cuando se edita un documento XML, al ser este de tipo texto, es
posible que, por legibilidad, se coloque cada uno de los elementos en líneas diferentes o incluso
que se utilicen espacios en blanco para separar los elementos y ganar en claridad.
DOM no distingue cuándo un espacio en blanco o un salto de línea (\n) se hace porque es un
texto asociado a un elemento XML o es algo que se hace por "estética". Por eso, DOM trata todo
lo que es texto de la misma manera, creando un nodo de tipo #text y poniéndole el valor dentro
de ese nodo. En diagrama anterior el nodo #text que desciende de Película y que tiene como
valor "\n" (salto de línea) es creado por DOM ya que, por estética, se ha hecho un salto de línea
dentro del documento XML, para diferenciar claramente que la etiqueta <Titulo> es

Tema 1: Manejo de ficheros 10


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

descendiente de <Pelicula>. Sin duda, en el documento XML hay muchos más "saltos de línea"
que se han empleado para dar claridad al documento, sin embargo, en el árbol no se han
incluido ya que tantos nodos con "saltos de línea" dejarían el esquema ilegible.
8. Por último, un documento XML tiene muchas más "cosas" que las mostradas en el ejemplo:
comentarios, encabezados, espacios en blanco, entidades, etc., son algunas de ellas. Cuando se
trabaja con DOM, rara vez esos elementos son necesarios para el programador, por lo que la
librería DOM ofrece funciones que omiten estos elementos antes de crear el árbol, agilizando así
el acceso y modificación del árbol DOM.
2.1.1. DOM y Java
DOM ofrece una manera de acceder a documentos XML tanto para ser leído como para ser modificado.
Su único inconveniente es que el árbol DOM se crea todo en memoria principal, por lo que si el
documento XML es muy grande, la creación y manipulación de DOM sería intratable.
Muchas han sido las propuestas para trabajar desde Java con la gran APLICACIÓN
cantidad de parsers DOM que existen. Sin embargo, para resumir,
actualmente la propuesta principal se reduce al uso de JAXP (Java API for JAXP
XML Processing). A través de JAXP los diversos parsers garantizan la
interoperabilidad de Java. Una aplicación que desea manipular documentos PARSER DE OTROS
XML accede a la interfaz JAXP y a través de esta puede utilizar de manera REFERENCIA PARSERS
transparente los diferentes parsers que hay para manejar XML. Estos
parsers son para DOM (XT, Xalan, Xerces, …) pero también pueden ser parsers para SAX.
En los ejemplos mostrados en las siguientes secciones se utilizará la librería Xerces para procesar
representaciones en memoria de un documento XML considerando un árbol DOM. Entre los paquetes
concretos que se usarán destacan:
 javax.xml.parsers.*4, en concreto javax.xml.parsers.DocumentBuilder y javax.xml.parsers.
DocumentBuilderFactory, para el acceso desde JAXP.
 org.w3c.dom.*5 que representa el modelo DOM según la W3C (objetos Node, NodeList,
Document, etc. y los métodos para manejarlos).
La Figura siguiente muestra un esquema que relaciona JAXP con el acceso a DOM. Desde la interfaz JAXP
se crea un DocumentBuilderFactory. A partir de esa factoría se crea un DocumentBuilder que permitirá
cargar en él la estructura del árbol DOM (Árbol de Nodos) desde un fichero XML (Entrada XML).

Interfax JAXP
DocumentBuilderFactory factory=
DocumentBuilderFactory.newInstance()

Crear

Traductor XML Árbol de Nodos


Entrada XML Procesar Objetos
DocumentBuilder builder= java Document arbol=
File f=new File() factory.newDocumentBuilder() builder.parse(f)

4
Más información en http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/package-summary.html
5
Más información en http://docs.oracle.com/javase/8/docs/api/org/w3c/dom/package-summary.html

Tema 1: Manejo de ficheros 11


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

La clase DocumentBuilderFactory tiene métodos importantes para indicar qué interesa y qué no interesa
del fichero XML para ser incluido en el árbol DOM, o si se desea validar el XML con respecto a un
esquema. Algunos de estos métodos son los siguientes:
 setIgnoringComments: sirve para ignorar los comentarios que tenga el fichero XML.
 setIgnoringElementContentWhitespace: es útil para eliminar los espacios en blanco que no
tienen significado (Solo si esta activada la validación del documento XML).
 setValidating: que valida el documento XML según el esquema definido.
Con respecto a los métodos que sirven para manipular el árbol DOM, que se encuentran en el paquete
org.w3c.dom, destacan los siguientes métodos asociados a la clase Node6:
 Todos los nodos contienen los métodos getFirstChild() y getNextSibling() que permiten obtener
uno a uno los nodos descendientes de un nodo y sus hermanos.
 El método getNodeType()7 devuelve una constante para poder distinguir entre los diferentes
tipos de nodos: nodo de tipo Elemento (ELEMENT_NODE), nodo de tipo #text (TEXT_NODE), etc.
Este método y las constantes asociadas son especialmente importantes a la hora de recorrer el
árbol ya que permiten ignorar aquellos tipos de nodos que no interesan (por ejemplo, los #text
que tengan saltos de línea).
 El método getAttributes() devuelve un objeto NamedNodeMap (una lista con sus atributos) si el
nodo es del tipo Elemento.
 Los métodos getNodeName() y getNodeValue() devuelven el nombre y el valor de un nodo de
forma que se pueda hacer una búsqueda selectiva de un nodo concreto. El error típico es creer
que el método getNodeValue() aplicado a un nodo de tipo Elemento (por ejemplo, <Titulo>)
devuelve el texto que contiene. En realidad, es el nodo de tipo #text (descendiente de un nodo
tipo Elemento) el que tiene el texto que representa el título de la película y es sobre él sobre el
que hay que aplicar el método getNodeValue() para obtener el título.
2.1.2. Generar árbol DOM
Para abrir un documento XML desde Java y crear un árbol DOM con él se utilizan las clases
DocumentBuilderFactory y DocumentBuilder, que pertenecen al paquete javax.xml.parsers, y Document,
que representa un documento en DOM y pertenece al paquete org.w3c.dom. Aunque existen otras
posibilidades, en el ejemplo mostrado seguidamente se usa un objeto de la clase File para indicar la
localización del archivo XML.
Ejemplo 10

import java.io.File;
import org.w3c.dom.Document;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class DOM {
public Document doc=null; // Objeto Document que almacena el DOM del XML seleccionado.
public void getDom(File f) {
try {
// Se crea un objeto DocumentBuiderFactory.
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
System.out.println(factory.getClass()); // informacion sobre la clase usada

6
Más información sobre la interfaz Node en: http://docs.oracle.com/javase/8/docs/api/org/w3c/dom/Node.html
7
Para ver los valores de las contantes en: http://docs.oracle.com/javase/8/docs/api/constant-values.html#org.w3c

Tema 1: Manejo de ficheros 12


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

// Indica que el modelo DOM no debe contemplar los comentarios que tenga el XML
factory.setIgnoringComments(true);
// Ignora los espacios en blanco. Si no se hace esto entonces los
// espacios en blanco aparecen como nodos.
factory.setIgnoringElementContentWhitespace(true);
// Se crea un objeto DocumentBuilder para cargar en él la estructura
// de árbol DOM a partir del XML seleccionado.
DocumentBuilder builder = factory.newDocumentBuilder();
// Interpreta (parsea) el documento XML (file) y genera el DOM equivalente.
this.doc=builder.parse(f);
//doc=builder.parse("http://www.w3schools.com/xml/simple.xml");

// Ahora doc apunta al árbol DOM listo para ser recorrido.


} catch (Exception e) {
System.out.println("Error generenado arbol DOM"+e.getMessage());
}
}
}

El método parse() de DocumentBuilder recibe como entrada un File con la ruta del fichero XML que se
desea abrir y devuelve un objeto de tipo Document. Este objeto (doc en el ejemplo) es el árbol DOM
cargado en memoria y listo para ser tratado.
2.1.3. Recorrer un árbol DOM
Para recorrer un árbol DOM se utilizan las clases Node y NodeList que pertenecen al paquete
org.w3c.dom.
El siguiente código recorre el árbol DOM mostrando el director y el título de cada película junto con los
posibles atributos que tenga.
Ejemplo 10: Continuación
public String muestraDOM(Document doc) {
String salida = "";
Node nodo, ntemp = null;
// Obtiene el primero nodo del DOM
Node raiz = doc.getFirstChild();
// Element raiz = doc.getDocumentElement();
// Obtiene una lista de nodos con todos los nodos hijo.
NodeList nodelist = raiz.getChildNodes();
for (int i = 0; i < nodelist.getLength(); i++) { // Proceso los nodos hijo
nodo = nodelist.item(i);
// Al ejecutar este código, se observa como los nodos que encuentra son de tipo 1
// (ELEMENT_NODE) y tipo 3 (TEXT_NODE).
// Es un nodo película que hay que procesar si es de tipo Elemento
if (nodo.getNodeType() == Node.ELEMENT_NODE) {
salida += "----------------------------------\n";
NodeList nodosHijos = nodo.getChildNodes();
for (int j = 0; j < nodosHijos.getLength(); j++) {
ntemp = nodosHijos.item(j);
// Se debe comprobar el tipo de nodo que se quiere tratar por que es posible
// que haya nodos tipo TEXT que contengan retornos de carro al cambiar de
// línea en el XML.

Tema 1: Manejo de ficheros 13


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

if (ntemp.getNodeType() == Node.ELEMENT_NODE) {
// IMPORTANTE: para obtener el texto con el título accede al
// nodo TEXT hijo de ntemp y saca su valor.
salida += ntemp.getNodeName() + ": "
+ ntemp.getChildNodes().item(0).getNodeValue() + "\n";
}
}

//obtengo los atributos del nodo


if (nodo.hasAttributes()) {
NamedNodeMap atributos = nodo.getAttributes();
for (int k = 0; k < atributos.getLength(); k++)
salida += "Atributo: "+ atributos.item(k).getNodeName() + " -> "
+ atributos.item(k).getNodeValue() + "\n";
}
}
}
return salida;
}

Es una práctica común al trabajar con DOM comprobar el tipo del nodo que se está tratando en cada
momento ya que, como se ha comentado antes, un DOM tiene muchos tipos de nodos que no siempre
tienen información que merezca la pena explorar. En el código, solo se presta atención a los nodos de
tipo Elemento (ELEMENT_NODE) y de tipo #Text (TEXT_NODE).
Si queremos obtener todos los nodos de un tipo determinado de elemento (por ejemplo película)
podemos usar el método getElementsByTagName el cual devuelve una lista de nodos de ese elemento.
NodeList nodelist = doc.getElementsByTagName("Película");

En el ejemplo se ha recorrido el árbol DOM creado del documento XML. El resultado de procesar dicho
documento origina la siguiente salida:
----------------------------------
Titulo: Dune
Director: David Lynch
Atributo: fechaEstreno -> 1984
----------------------------------
Titulo: Blade Runner
Director: Ridley Scott
Atributo: fechaEstreno -> 1982
----------------------------------
Titulo: Alien: el octavo pasajero
Director: Ridley Scott
Atributo: fechaEstreno -> 1979

Tema 1: Manejo de ficheros 14


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

2.1.4. Modificar y serializar un árbol DOM


Un árbol DOM puede ser modificado y guardado en un fichero para hacerlo persistente. En esta sección
se muestra un código para añadir un nuevo elemento a un árbol DOM y luego guardar todo el árbol en
un documento XML..
El siguiente código añade una nueva película al árbol DOM (doc) con los valores de fechaEstreno, título y
director, pasados como parámetros.

Ejemplo 10: Continuación


public int annadirDOM(String titulo, String director, String anno) {
try {
// Se crean los nodos en la estructura DOM apuntada por la variable doc.
// Se crea un nodo tipo Element con nombre titulo(<titulo>)
Element nodoTitulo = doc.createElement("Titulo");

// Se crea un nodo tipo texto con el título de la película


Text textNodoTitulo = doc.createTextNode(titulo);

// Se añade el nodo de texto con el título como hijo del elemento Titulo
nodoTitulo.appendChild(textNodoTitulo);

// Se hace lo mismo que con título para director (<director>)


Element nodoDirector = doc.createElement("Director");
Text textNodoDirector = doc.createTextNode(director);
nodoDirector.appendChild(textNodoDirector);

// Se crea un nodo de tipo elemento (<Película>)


Element nodoPelicula = doc.createElement("Pelicula");

// Al nuevo nodo película se le añade un atributo fechaEstreno


nodoPelicula.setAttribute("fechaEstreno", anno);

// Se añade a película un nodo tipo texto con un retorno de carro (\n) para que
// al abrirlo con un editor de texto cada nodo salga en un línea diferente.
nodoPelicula.appendChild(doc.createTextNode("\n"));

// Se añade a película el nodo título


nodoPelicula.appendChild(nodoTitulo);

// Se añade también un nodo retorno de carro \n


nodoPelicula.appendChild(doc.createTextNode("\n"));

// Se añade a película el nodo película.


nodoPelicula.appendChild(nodoDirector);

// Finalmente, se obtiene el primer nodo del documento y se le añade como hijo


// el nodo película que ya tiene colgando todos sus hijos y atributos creados.
Node raiz = doc.getChildNodes().item(0);
raiz.appendChild(nodoPelicula);

return 0;
} catch (Exception e) {
e.printStackTrace();
return -1;
}
}

Tema 1: Manejo de ficheros 15


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

Una vez se ha modificado en memoria un árbol DOM, éste puede ser llevado a fichero para lograr la
persistencia de los datos. Esto se puede hacer de muchas maneras. Una alternativa concreta se recoge
en el siguiente código.
Ejemplo 10: Continuación
public void grabaDOM(Document document, FileOutputStream ficheroSalida) throws
ClassNotFoundException, InstantiationException, IllegalAccessException {
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementationLS ls=(DOMImplementationLS) registry.getDOMImplementation("XML 3.0 LS 3.0");
// Creamos un destino vacio
LSOutput output = ls.createLSOutput();
output.setEncoding("UTF-8");
// Establecemos el fujo de salida
output.setByteStream(ficheroSalida);
//output.setByteStream(System.out);
// Permite estribir un documento DOM en XML
LSSerializer serializer = ls.createLSSerializer();
//Establecemos propiedades del serializador
serializer.setNewLine("\r\n");;
serializer.getDomConfig().setParameter("format-pretty-print", true);
// Escribimos el documento ya sea en un fichero o en una cadena
serializer.write(document, output);
// String xmlCad=serializer.writeToString(document);
}

2.2. SAX
SAX (Simple API for XML) es otra tecnología para poder acceder a XML desde lenguajes de programación.
Aunque SAX tiene el mismo objetivo que DOM esta aborda el problema desde una óptica diferente.
Las principales características de SAX son:
 SAX abre y recorre secuencialmente el fichero XML. Terminado el proceso cuando llega al final
del mismo. En SAX el programador no tiene una visión global del documento y no puede
moverse por el mismo a su antojo.
 SAX, a diferencia de DOM, no carga el documento en memoria, sino que lo lee directamente
desde el fichero. Esto provoca que use muy poca memoria lo que lo hace especialmente útil
cuando los ficheros XML son muy grandes.
 La lectura de un documento XML produce eventos que ocasiona la llamada a métodos.

SAX sigue los siguientes pasos básicos:


 SAX abre un archivo XML y coloca un puntero en al comienzo del mismo.
 Cuando comienza a leer el fichero, el puntero va avanzando secuencialmente.
 Cuando SAX detecta un elemento propio de XML entonces lanza un evento. Un evento puede
deberse a:
 Que SAX haya detectado el comienzo del documento XML.
 Que se haya detectado el final del documento XML.

Tema 1: Manejo de ficheros 16


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

 Que se haya detectado una etiqueta de comienzo de un elemento, por ejemplo <película>.
 Que se haya detectado una etiqueta de final de un elemento, por ejemplo </película>.
 Que se haya detectado un atributo.
 Que se haya detectado una cadena de caracteres que puede ser un texto.
 Que se haya detectado un error (en el documento, de I/O, etc.).
 Cuando SAX devuelve que ha detectado un evento, entonces este evento puede ser manejado
con la clase DefaultHandler8 (callbacks). Esta clase debe ser extendida y los métodos de esta
clase deben ser redefinidos (sobreescritos) por el programador para conseguir el efecto deseado
cuando SAX detecta los eventos.
 Cuando SAX detecta un evento de error o un final de documento entonces se termina el
recorrido.
2.2.1. Abrir XML con SAX desde Java
Para abrir un documento XML desde Java con SAX y utilizando JAXP se utilizan las clases:
SAXParserFactory y SAXParser que pertenecen al paquete javax.xml.parsers.
Ejemplo 11
public void manejador(File ficheroXML) throws
ParserConfigurationException, SAXException, IOException {
SAXParserFactory factory = SAXParserFactory.newInstance();

// Se crea un objeto SAXParser para interpretar el documento XML.


SAXParser parser = factory.newSAXParser();

// Se crea un instancia del manejador que será el que recorra el documento


// XML secuencialmente
ManejadorSAX manejador = new ManejadorSAX();

// SE lanza el parser que recorrerá secuencialmente el documento XML y cuando


// detecte un comienzo o fin de elemento o un texto entonces lo tratará según
// la implementación hecha del manejador
parser.parse(ficheroXML, manejador);
}

Como se puede entender siguiendo los comentarios del código, primeramente se crean los objetos
factory y parser. Además SAX, a diferencia con DOM, debe crear una instancia de una clase que extienda
a DefaultHandler (en el ejemplo ManejadorSAX) que redefine los métodos que personalizan el
comportamiento del parser cuando se produce un evento
El método parse lanza SAX para el fichero XML seleccionado y con el manejador deseado (se podrían
implementar tantas extensiones de DefaultHandler como manejadores diferentes se quisieran usar).
2.2.2. Recorrer XML con SAX
Para poder lanzar el parser antes es necesario haber definido la clase que extiende DefaultHandler.
Esta clase extiende, entre otros, los métodos startDocument, endDocument, startElement, endElement y
characters. Estos métodos (callbacks) se invocan cuando, durante el recorrido del documento XML, se
detecta un evento de comienzo del documento, fin del documento, comienzo de elemento, final de
elemento o cadena de caracteres.

8
Ver más en http://docs.oracle.com/javase/8/docs/api/org/xml/sax/helpers/DefaultHandler.html

Tema 1: Manejo de ficheros 17


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

En el ejemplo que veremos a continuación, cada método tiene las siguientes funciones:
 startDocument: cuando se detecta con SAX un evento de comienzo de un documento, entonces
SAX invoca a este método. En el ejemplo muestra solamente un mensaje informativo.
 endDocument: SAX invoca este método cuando detecta el final del documento.
 startElement: cuando se detecta con SAX un evento de comienzo de un elemento, entonces SAX
invoca a este método. En el ejemplo lo que hace es comprobar de qué tipo de elemento se trata:
 Si es <Pelicula> entonces saca el valor de su atributo 9 y lo concatena con una cadena
(cadena_resultado) que tendrá toda la salida después de recorrer todo el documento.
 Si es <Titulo> muestra el título del película.
 Si es <Director> muestra el director del película.
 Si es otro tipo de elemento no hará nada.
 endElement(): cuando se detecta con SAX un evento de final de un elemento, entonces SAX
invoca a este método. El en ejemplo el método comprueba si es el final de un elemento
<Pelicula>. Si es así, entonces muestra una línea para separar las películas.
 characters(): cuando se detecta con SAX un evento de detección de cadena de texto, entonces
SAX invoca a este método. El método lo que hace en el ejemplo es limpiar la cadena leída y si
tiene contenido mostrarlo..
Un ejemplo de clase ManejadorSAX es el siguiente:
Ejemplo 11: Continuación
class ManejadorSAX extends DefaultHandler {

@Override
public void startDocument() {
System.out.println("Comienzo del Documento XML");
}

@Override
public void endDocument() {
System.out.println("Final del Documento XML");
}

@Override
public void startElement(String uri, String localName, String qName, Attributes atts)
throws SAXException {
if (qName.equals("Pelicula")) {
for (int i=0;i<atts.getLength();i++)
System.out.println(atts.getLocalName(i)+":\t"+atts.getValue(i));
}
else if (qName.equals("Titulo")) System.out.print("El título es: ");
else if (qName.equals("Director")) System.out.print( "El director es: ");
}

@Override
public void endElement(String uri, String localName, String qName) throws
SAXException {
if (qName.equals("Director")) System.out.println("-------------------------");
}

9
Ver métodos disponibles en http://www.saxproject.org/apidoc/org/xml/sax/Attributes.html

Tema 1: Manejo de ficheros 18


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

@Override
public void characters(char[] ch, int inicio, int longitud) throws SAXException {
String car=new String(ch, inicio, longitud);
car = car.replaceAll("[\t\n]",""); //quitar saltos de línea y tabuladores
if (car.trim().length()>0) System.out.println ("\t" + car);
}

@Override
public void error(SAXParseException e) {
System.out.println("Error: "+e.getMessage());
}

@Override
public void fatalError(SAXParseException e) {
System.out.println("Error fatal: "+e.getMessage());
}
}

Si se aplica el código anterior al contenido del documento XML, el resultado sería el siguiente:
Comienzo del Documento XML
fechaEstreno: 1984
El título es: Dune
El director es: David Lynch
-------------------------
fechaEstreno: 1982
El título es: Blade Runner
El director es: Ridley Scott
-------------------------
fechaEstreno: 1979
El título es: Alien: el octavo pasajero
El director es: Ridley Scott
-------------------------
Final del Documento XML

Tema 1: Manejo de ficheros 19


RAMA: Informática CICLO: Desenvolvemento de Aplicacions Multiplataforma
MÓDULO Acceso a datos CURSO: 2º
PROTOCOLO: Apuntes clases AVAL: 1 DATA: 2016/2017
UNIDAD COMPETENCIA

2.3. Crear un documento XML con StAX


SAX no proporciona la funcionalidad de escribir documentos XML. Para ello usaremos StAX (Streaming
API for XML).
Creamos un objeto XMLStreamWriter a partir de XMLOutputFactory que nos permite escribir los
elementos que van a componer nuestro documento XML.
Un ejemplo que muestra la utilización de algunos los métodos disponibles es el siguiente:
Ejemplo 12
public void creaXml (File f) throws XMLStreamException, IOException {
XMLOutputFactory factory = XMLOutputFactory.newInstance();
XMLStreamWriter writer = factory.createXMLStreamWriter(new FileWriter(f));

writer.writeStartDocument();
writer.writeStartElement("peliculas");
writer.writeStartElement("pelicula");
writer.writeAttribute("fechaEstreno", "1979");
writer.writeAttribute("duracion", "132");
writer.writeStartElement("Titulo");
writer.writeCharacters("Star Trek");
writer.writeEndElement();
writer.writeStartElement("Director");
writer.writeCharacters("Robert Wise");
writer.writeEndElement();
writer.writeEndElement();
writer.writeEndElement();
writer.writeEndDocument();

writer.flush();
writer.close();
}

Tema 1: Manejo de ficheros 20

También podría gustarte