Buscar
Social
Ofertas laborales ES
« Llega Cocoon 2 | Main | Log4java (Parte 1) »
viernes
jun012001

Mapeo de XML a Java (Parte 2 - final)


Mapeo de XML a Java (parte 2)


Fecha de creación: 01.06.2001

Revisión 1.0.1 (15.10.2002)

Alberto Molpeceres
al AT javahispano DOT org

Copyright (c) 2002, Alberto Molpeceres. Este documento puede ser distribuido solo bajo los términos y condiciones de la licencia de Documentación de javaHispano v1.0 o posterior (la última versión se encuentra en /licencias/).



Introducción


En este articulo referente al API SAX ampliamos el anterior [1], de forma que tendremos un conocimiento más profundo del interface ContentHandler, básico al trabajar con SAX, y aprenderemos como manipular ficheros XML más complejos que en el ejemplo anterior.


Se espera haber leido y comprendido el artícuo anterior, lo que implica un cierto conocimiento de XML, su estructura y su funcion.



El interface ContentHandler


El interface ContentHandler define los siguientes métodos, que como dijimos se ejecutarán cada vez que ocurra un evento al procesar el documento XML:



public void setDocumentLocator (Locator locator)

Este método recive una instancia de la clase Locator que contendrá la información referente a la posición del docuemtno donde sucede un evento.



public void startDocument () throws SAXException

Simple. El evento que se produce al comenzar a procesar un docuemtno XML.



public void endDocument () throws SAXException

Fin del documento XML



public void processingInstruction (String target, String data)
throws SAXException

Este evento se produce cuando se encuentra una instrucción de proceso de XML (llamadas también PI) distinta al a declaración de decomento XML, como por ejemplo<? Cocoon process type = "xslt" ?>. No entraré a explicar en detalle lo que es una PI, ya que esto no es tutorial de XML, simplemente diré que es una especie de instrucción para ciertos programas que procesas XML. Los argumentos de este método son el destino de la PI y los atributos de dicha instrucción.



public void startPrefixMapping (String prefix, String uri)

Indica el comienzo de un prefijo de un espacio de nombre (Namespace), cuya función también se escapa de los objetivos de este articulo, aunque se puede definir como una forma de identificar inequivocamente los elementos del documento. Los parametros de éste método son el prefijo del espacio de nombres y su dirección URI asociada.



public void endPrefixMapping (String prefix)

Indica cuando se ha terminado la petición de inicio del prefijo del espacio de nombres.



public void startElement (String namespaceURI, String localName,
String rawName, Attributes atts)
throws SAXException

Este método se ejecuta cuando empieza un elemento del documento XML. Los argumentos que recibe son la dirección URI del espacio de nombres asociado al elemento, el nombre del elemento (o etiqueta) sin el prefijo del espacio de nombres, el nombre del elemento en la versión 1.0 de la especificación de XML, y los atributos que contiene la etiqueta en forma de una instancia de la clase Attributes.



public void endElement (String namespaceURI, String localName,
String rawName)

Se produce al teminar un elemento. El sgnificado de los atributos es el mismo que en startElement.



public void characters (char[] ch, int start, int length)
throws SAXException

Este método consigue el valor del elemento, es decir, el contenido entre las etiquetas de inicio y final del elemento. Los parametros que recibe son claros, un array de caracteres, asi como la indicación del inicio y la extension del elemento.



public void ignorableWhitespace (char[] ch, int start, int length)
throws SAXException

Este método indica los espacios en blanco que pueden ser ignorados en el documento, pero normalmente solo se utiliza cuando se valida el documento durante el procesamiento del fichero. Los parametros tienes el mismo significado que en el método anterior, characters.



public void skippedEntity (String name) throws SAXException

Este método indica que una entidad externa se ha ignorado al procesar el fichero (algo que n pasará en un parser estable como es el Xerces). Recibe como parametros el nombre de dicha entidad.


Volviendo a la prática


Y después de toda esta cantidad de palabrería vamos con algo práctico. Como anunciá en el artículo anterior, vamos a ver que hacer cuando en un documento XML existen elementos anidados del estilo de:


Punto de partida



<?xml version="1.0"?>
<clientes>
<empresa>
<nombre> GFT Technologies </nombre>
<direccion> Gran Vía 1 </direccion>
. . .
<persona-contacto>
<nombre> Alberto Molpeceres </nombre>
<cargo> Jefe </cargo>
. . .
</persona-contacto>
</empresa>
</clientes>



Cuando esto ocurre, normalmente es porque son entidades distintas, objetos distintos, por lo tanto clases de java distintas. Una solución sería llenar nuestro ContentHandler de estructuras if y de variables internas que controlasen donde estamos en cada momento, pero eso no sería muy elegante. La solución que aplicaré aqui consistirá en tener varios ContentHandler y pasar el control (y por tanto el proceso del fichero) de uno a otro según se cambie de entidad.


Suponiendo el documento XML anterior, y las siguientes clases:

public class Persona
{
private String nombre;
private String cargo;
. . .
public Persona (){}
. . .
public void setNombre (String nombre)
{
this.nombre = nombre;
}
. . .
public void setCargo (String cargo)
{
this.cargo = cargo;
}
. . .
}

public class Empresa
{
private String nombre;
private String direccion;
. . .
private Persona contacto;
. . .
public Empresa (){}
. . .
public void setNombre (String nombre)
{
this.nombre = nombre;
}
. . .
public void setDireccion (String direccion)
{
this.direccion = direccion;
}
. . .
public void setPersonaContacto (Persona contacto)
{
this.contacto = contacto;
}
. . .
}



Para mapear el fichero tendremos que crear un ContentHandler para cada clase y la lógica de control necesaria para pasar de uno a otro en tiempo de ejecución.


El primer ContentHandler, o principal, pasará el control al segundo al llegar al comienzo de la etiqueta <persona-contacto>, que lo devolverá al primero al llegar a la de fin de entidad </persona-contacto>. Un ejemplo vale más que mil palabras, así que aqui teneis la solución de nuestro problema con los nombres.como soy un poco vago, y ya que no necesitamos todos los métodos, en lugar de implementar el interface ContentHandler extendere el manejador por defecto, DefaultHandler. La idea es la misma, todo depende de lo que necesites.


Handler para empresas


Este es el ContentHandler principal, con el que comienza el proceso, en de las empresas.

import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
import java.util.Vector;

public class EmpresaXMLHandler extends DefaultHandler
{
/* Esto no es nuevo */
//vector de instancias
private Vector instancias;
//"Empresa" que se esta procesando
private Empresa actual;
//valor contenido entre las etiquetas de un elemento
private String valor;

/* Esto si es nuevo */
//Como queremos pasar el control del proceso de un
//Handler a otro, necesitamos tener el parser para
//asignarle el Handler que necesite en cada instante.
private XMLReader parser;
//Este es el Handler que procesara las personas que
//contenga el documento
private PersonaXMLHandler handlerPersona;

public EmpresaXMLHandler (XMLReader parser, Vector v)
{
this.parser = parser;
this.instancias = v;
}

public void startElement( String namespaceURI, String localName, String qName,
Attributes attr ) throws SAXException
{
//comprobamos si empezamos un elemento "pagina"
if (localName.equals("empresa")){
//creamos la nueva instancia de Empresa
actual = new Empresa ();
//y la añadimos al Vector qie las almacena
instancias.addElement (actual);
}
else if (localName.equals("persona-contacto")){
//creamos una instancia de Persona
Persona contacto = new Persona ();
//se la asignamos a la empresa actual como contacto
actual.setPersonaContacto (contacto);
/*
y pasamos el control al Handler de persona contacto
le tenemos que pasar el parser, para que recoga
los datos del fichero, este Handler para que luego
nos devuelva el control, y la persona donde meter
los datos que procese.
*/
handlerPersona = new HandlerPersona( parser, this, contacto );
parser.setContentHandler( handlerPersona );
}
}

public void endElement (String namespaceURI, String localName, String rawName)
throws SAXException
{
/*
miramos de que elemento se trata y asignamos los atributos
correspondientes a la "Empresa" actual.
*/
if (localName.equals("nombre")){
actual.setNombre (valor);
}
else if (localName.equals("direccion")){
actual.setDireccion (valor);
}
valor = null;
}


/*
Los parametros que recibe es la localizacion de los carateres del elemento.
*/
public void characters (char[] ch, int start, int end) throws SAXException
{
//creamos un String con los caracteres del elemento y le quitamos
//los espacios en blanco que pueda tener en los extremos.
valor = new String (ch, start, end);
valor = valor.trim();
}

}



Handler para personas


Ahora el ContentHandler auxiliar que se encargará de procesar la información de las personas de contacto.

import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;

public class PersonaXMLHandler extends DefaultHandler
{
//valor contenido entre las etiquetas de un elemento
private String valor;

//El parser, para luego devolver el control al
//Handler de empresas.
private XMLReader parser;
//El Handler al que queremos volver
private EmpresaXMLHandler handlerEmpresa;
//La persona donde meter los datos que leamos.
private Persona persona;

public PersonaXMLHandler (XMLReader parser, EmpresaXMLHandler handler,
Persona persona)
{
this.parser = parser;
this.handlerEmpresa = handler;
this.persona = persona;
}

public void endElement (String namespaceURI, String localName, String rawName)
throws SAXException
{
/*
miramos de que elemento se trata y asignamos los atributos
correspondientes a la "Persona" actual o devolvemos el control
al Handler de empresas.
*/
if (localName.equals("nombre")){
persona.setNombre (valor);
}
else if (localName.equals("cargo")){
actual.setCargo (valor);
}
//si hemos llegado al final de la etiqueta
else if (localName.equals("persona-contacto")){
parser.setContentHandler (handlerEmpresa);
}
valor = null;
}


/*
Los parametros que recibe es la localizacion de los carateres del elemento.
*/
public void characters (char[] ch, int start, int end) throws SAXException
{
//creamos un String con los caracteres del elemento y le quitamos
//los espacios en blanco que pueda tener en los extremos.
valor = new String (ch, start, end);
valor = valor.trim();
}

}



Lanzando el programa


Por ultimo nos queda la clase que empezaría todo, la clase del main. Exactamente igual que la del articulo anterior, así que sobran los comentarios.

import java.util.Vector;

import org.xml.sax.XMLReader;
import org.xml.sax.SAXException;

import org.apache.xerces.parsers.SAXParser;

public class Test
{
Vector instancias = new Vector ();

public Test()
{
}

public void procesarFichero ()
{
try
{
XMLReader parser = new SAXParser();
parser.setContentHandler(new MiXMLHandler(parser, instancias));
parser.parse("misClientes.xml");
}
catch (Exception e)
{
System.out.println ("Error al procesar el fichero de Clientes: "
+ e.getMessage());
e.printStackTrace();
}
}


public static void main(String[] args)
{
Test test = new Test();

test.procesarFichero();
}
}




Conclusión


Pues esto era todo, como veis con dos articulos hemos tenido más que suficiente para mapear los datos de un fichero XML a clases de Java, pero me temo que no todo es así de sencillo, esto solo es una base de iniciación.


Para documentos más complejos ahí teneis todos los métodos del interface ContentHandler, y para manipilaciones más complicadas de XML, donde no llega SAX llega DOM/JDOM. Pero eso es otra historia.



Recursos




[1] ,
/articulos/ver_articulo.jsp?id=2


Acerca del autor

Alberto Molpeceres
Alberto es es ahora mismo desarrollador de aplicaciones en ámbito cliente/servidor para la empresa T-Systems - debis Systemhaus en Munich (Alemania). Cuando no está trabajando o "metiendo caña" al resto de los integrantes de javaHispano intenta pasear con su novia, buscar la desaparecida lógica del idioma alemán o intentar olvidar la pesadilla que es buscar piso en Munich.

Reader Comments

There are no comments for this journal entry. To create a new comment, use the form below.
Comentarios deshabilitados
Comentarios deshabilitados en esta noticia.