sábado, 9 de agosto de 2008

Rumore, rumore

Vamos a cambiar un poco la temática. Esta vez escribo sobre páginas curiosas en internet. ¿Nunca os ha llegado un correo alertando sobre un peligro y os habéis preguntado si realmente será cierto o no? La verdad es que yo soy bastante reticente a creer ese tipo cosas ya que en la mayoría de los casos lo que se pretende es crear una cadena de correos. Hoy sin embargo, me he topado con una página donde se coleccionan rumores y leyendas urbanas de todo tipo. Para mi sorpresa había algún rumor que yo tenía como falso y parece ser cierto. En este post expongo algunos rumores conocidos o que me han impactado.

Probablemente ya conozcas esta página pues por lo que he visto es bastante conocida, sobre todo en Estados Unidos. www.snopes.com es una página dedicada a los rumores y leyendas urbanas. Están clasificadas por temática y en la mayoría especifican si es verdadera o falsa. En otras no es posible, pero siempre dan una amplia explicación acerca del rumor.

El hombre usa tan sólo el 10% de su cerebro

Seguro que has escuchado esto infinidad de veces. Al parecer es totalmente falso. Parece ser que los medios de comunicación y sobre todo la parapsicología han engrandecido este mito para su autopromoción. Me hizo bastante gracia una frase que dice el artículo: Mientras que para la gente que repite el mito, es probablemente cierto, afortunadamente el resto de nosotros usamos todo nuestro cerebro.

El agua calentada en un microondas puede explotar repentinamente

Hace tiempo me llego un correo, de esos que forman una cadena, alertándome de esto. Lo cierto es que no me lo creí mucho, pero poco tiempo después le ocurrió a alguien cercano. Según snopes, es cierto pero ocurre muy de vez en cuando. Yo puedo dar fe de ello. Así que cuidado con calentar demasiado el agua en el microondas.

El virus de la antorcha olímpica

Este me hizo mucha gracia porque he recibido el correo varias veces. Para el que no lo haya recibido nunca (dudo que haya muchos), se trata de un correo que te advierte sobre otro correo que te podrá llegar en varios días con el título Invitation, Merry Chirstmas o Olympic Torch (hay varias variantes). Según cuenta el correo se trata de un virus que es una antorcha olímpica y te quema todo el disco duro, :-p jaja

Yo entiendo que haya gente que no entienda mucho de informática y pueda creer que esto vaya a ocurrir, pero es que lo he recibido de gente que ES informática. Por dios, si nada más ver como está redactado se ve que es de coña. Que vale que pueda ser un virus, pero ¡que te queme el disco duro con una antorcha! .... en fin. Hay gente muy precavida que reenvía.

Por supuesto, es falso.

Un estudio de la OMS concluye que el rubio natural se extingirá en unos 200 años

Este fue el mito que me hizo descubrir esta página. Además no fue en internet, sino que estaba leyendo un libro y al terminar tenía una bibliografía y entre ella venía este artículo. El libro es Next, de Michael Crichton y lo recomiendo a todo el que le guste la lectura.

El rumor es falso. Al parecer la OMS nunca realizo tal estudio, simplemente se lo atribuyeron por la cara.

Regreso al Futuro II predice el ganador de la World Series de 1997 de Baseball

¿Os acordáis de Regreso al Futuro? Me encanta esa trilogía. Biff roba el almanaque deportivo en el futuro y vuelve al pasado para hacerce rico. Corre el rumor de que en la película se dice que Florida gana la World Series en 1997, como ocurrió en realidad. Recordad que la película es de 1987. En 1987 Florida ni siquiera tiene equipo de baseball. Doble hazaña: adivinar el campeón y además el equipo campeón no existe en el momento de hacer la profecía.

Nada más lejos de la realidad, pues aunque Florida ganó en 1997, en la película se dice que fue Miami quién gana en 1997. Así que es falso.

Tu información personal puede ser obtenida en Zabasearch.com

Pues al parecer es cierto. Aunque sólo para ciudadanos estadounidenses. A ver si eres capaz de encontrar al famoso John Smith.


Walt Disney fue criogenizado tras su muerte para ser resucitado en el futuro

Es uno de los mitos más repetidos. Al parecer, falso.

Un tornado arrasa un autocine de Canada mientras estaban proyectando la película Twister

Parece coña, ¿verdad? Pues es cierto.

En el Titanic en el momento de chocar contra el iceberg, se estaba proyectando una versión muda de la película The Poseidon Adventure

Cierto. Un tanto irónico, ¿verdad?

El Rey Juan Carlos asesina accidentalmente a su hermano

Me sorprendió ver esto en una página americana. Desconocía este suceso, pero al parecer el hermano del Rey Juan Carlos, Don Alfonso, murió mientras jugaba con su hermano mayor, Juan Carlos, tras dispararse accidentalmente el arma del Rey. Tenían 14 y 18 años. Según snopes, sólo el Rey Juan Carlos sabrá lo que sucedió entonces. Lo califican como indeterminado.

El daño causado al pentágono el 11-S no fue causado por el secuestrado Boeing 757

Según la versión oficial, fue un Boeing 757 secuestrado por los terroristas el que se estrelló contra el pentágono causando los daños que todos hemos visto alguna vez. Hay teorías que mantienen que no fue un Boeing lo que se estrelló, pues si ves las imágenes no hay rastros del avión por ningún lado. Según snopes, este mito es falso, lo que otorga validez a la versión oficial.

Sin embargo, yo tengo mis dudas sobre ello. Sobre todo después de ver ciertos documentales como Zeitgeist. Si navegas un poco por internet puedes sacar tus propias conclusiones. Aquí te dejo un par de enlaces, pero hay muchos:

Realmente no me quedo ni con una cosa ni con la otra, pues igual que por una parte pienso que los intereses económicos son los que desgraciadamente manejan el mundo de hoy, también creo que es fácil crear rumores y mucho más si se trata de una teoría conspiratoria de esta magnitud. Lo que sí pienso es que no se ha contado toda la verdad y además creo que desgraciadamente, ésta nunca la sabremos. Hay muchas lagunas sobre este tema.

Recuerda, no creas todo lo que ves. La manipulación está hoy a la ordén del día.

viernes, 8 de agosto de 2008

Java y XML: JDOM

Este es otro post de la serie de artículos sobre Java y XML. Tras haber visto cómo trabajar con SAX y DOM, ahora toca el turno de hablar un poco de JDOM.

JDOM es una API desarrollada específicamente para Java y da soporte al tratamiento de XML: parseo, búsquedas, modificación, generación y serialización. Es un modelo similar a DOM, pero no está creado ni modelado sobre DOM. Se trata de un modelo alternativo. La principal diferencia es que DOM fue desarrollado para que fuera independiente del lenguaje, mientras que JDOM está creado y optimizado específicamente para Java. Esto imprime a JDOM las ventajas inherentes a Java, lo que lo hacen que sea una API más eficiente y más natural de usar para el desarrollador Java y por tanto requerie un menor coste de aprendizaje.

Al igual que DOM, JDOM genera un árbol de nodos al parsear un documento XML. En cuanto a lo tipos de nodos, son similares a los de DOM, aunque algunos cambia el nombre ligeramente. La jerarquía de clases también se ve algo modificada.

Mientras que en DOM todo hereda de la clase Node, en JDOM casi todo hereda de la clase Content, que se encuentran en el paquete org.jdom. Digo casi todo porque ni Document ni Attribute (Attr en DOM) heredan de Content. La razon es que Document no se considera un contenido, sino más bien el continente y Attribute tampoco se considera un nodo, sino una propiedad de los nodos tipo Element.

Heredan de Content: Element, Comment y Text. De este último, Text, hereda CDATA (CDATASection en DOM). Por supuesto, hay más clases que heredan de Content y que forman el árbol de nodos, pero no las veremos en estos ejemplos ya que su uso es menos frecuente: DocType, EntityRef y ProccessingInstruction. Estas o sus equivalentes también existen en DOM.

Otra diferencia entre DOM y JDOM es que mientras en DOM todos estos tipos de nodos eran interfaces, en JDOM son clases y por tanto para crear un objeto de cualquier tipo basta con usar el operador new. Por ejemplo,

Element etiqueta = new Element("nombre_etiqueta");
CDATA cdata = new CDATA("Contenido del CDATA");
Comment comentario = new Comment("Texto del comentario");

Antes de pasar a los ejemplos, voy a comentar qué hay que hacer para incluir JDOM en tu proyecto. Lo primero es descargar la última versión de JDOM, por ahora la 1.1. Descomprime el fichero y sigue las intrucciones de uso. Si eres de los seguidores de la ley del mínimo esfuerzo, simplemente tienes que copiar los siguientes ficheros al directorio lib de tu proyecto:
  • jdom.jar, el cual se encuentra en la carpeta build.
  • jaxen-core.jar, jaxen-jdom.jar y saxpath.jar, que se encuentran en la carpeta lib.
  • Es posible que necesites algunos otros jars que se encuentran en la carpeta lib. Al menos para los ejemplos publicados aquí no son necesarios.

Ahora sí, pasemos a lo interesante. Como siempre vamos a partir del siguiente XML:

<?xml version="1.0" encoding="UTF-8"?>
<etiquetaPrincipal xmlns:xela="http://www.latascadexela.es" >
<etiquetaHija id="1" atributo1="valorAtributo1" atributo2="valorAtributo2">
Texto dentro de la etiqueta hija
</etiquetaHija>
<!-- Comentario -->
<xela:etiquetaConNamespace descripcion="etiqueta con un namespace"/>
</etiquetaPrincipal>

Si tenéis cualquier duda podéis acudir al JavaDoc de JDOM. También podéis preguntarla aquí. Estaré encantado de ayudar en lo que pueda.

Parseando un XML

Una de las ventajas de SAX (o la única) es que procesa los ficheros XML por trozos y por tanto no necesita tener todo el XML en memoria. Esa ventaja la han aprovechado los de JDOM y para parsear el XML a partir de un fichero se usa el SAXBuilder. Internamente usará un SaxHandler que generará el árbol de nodos a partir del XML.

// Creamos el builder basado en SAX
SAXBuilder builder = new SAXBuilder();
// Construimos el arbol DOM a partir del fichero xml
Document documentJDOM = builder.build(new FileInputStream("/ruta_a_fichero/fichero.xml"));

Si lo que tenemos es un árbol de nodos DOM, es decir, que lo hemos generado con DOM, también podemos construir su equivalente JDOM usando la clase DOMBuilder.

DOMBuilder dombuilder = new DOMBuilder();
Document doc = dombuilder.build(documentDOM);

Recorriendo el árbol de nodos

Una vez obtenido el objeto Document ya podemos recorrer el árbol de nodos como gustemos. Un ejemplo de como recorrer las etiquetas hijas de la etiqueta raíz:

// Obtenemos la etiqueta raíz
Element raiz = doc.getRootElement();
// Recorremos los hijos de la etiqueta raíz
List<Element> hijosRaiz = raiz.getChildren();
for(Element hijo: hijosRaiz){
   // Obtenemos el nombre y su contenido de tipo texto
   String nombre = hijo.getName();
   String texto = hijo.getValue();
   
   System.out.println("\nEtiqueta: "+nombre+". Texto: "+texto);
   
   // Obtenemos el atributo id si lo hubiera
   String id = hijo.getAttributeValue("id");
   if(id!=null){
      System.out.println("\tId: "+id);
   }
}

También podemos obtener una etiqueta en concreto de entre las etiquetas hijas de otra.

// Obtenemos una etiqueta hija del raiz por nombre
Element etiquetaHija = raiz.getChild("etiquetaHija");

// Incluso podemos obtener directamente el texto de una etiqueta hija
String texto = raiz.getChildText("etiquetaHija");

y con namespaces

// Obtenemos una etiqueta hija del raiz por nombre con Namespaces
// Primero creamos el objeto Namespace
Namespace nsXela = Namespace.getNamespace("xela", "http://www.latascadexela.es");
// Ahora obtenemos el hijo
Element etiquetaNamespace = raiz.getChild("etiquetaConNamespace", nsXela);

Buscando con XPath

También podemos usar XPath con JDOM para buscar etiquetas dentro del XML. Si quieres saber más acerca de XPath, te remito al post de DOM.

En JDOM existe un objeto llamado XPath, el cuál ofrece dos métodos estáticos: selectSingleNode y selectNodes. Uno para evaluar expresiones que devuelvan un único resultado y otro para expresiones que devuelvan una lista de resultados. El primero es un caso particular del segundo, que es el caso genérico.

// Buscamos una etiqueta mediante XPath
Element etiquetaHijaXP = (Element)XPath.selectSingleNode(doc, "/etiquetaPrincipal/etiquetaHija");

y con namespaces

// Buscamos una etiqueta con namespace mediante XPath
Element etiquetaNamespaceXP = (Element)XPath.selectSingleNode(doc, "/etiquetaPrincipal/xela:etiquetaConNamespace");

Si vamos a evaluar una misma expresión XPath sobre varios objetos del XML, como por ejemplo varios Document, es más eficiente construirnos un objeto XPath con esa expresión y aplicarlo tantas veces como deseemos. Para ello usamos el método estático newInstance de XPath.

// Si hacemos uso muchas veces del mismo XPath sobre varios document
// es más eficiente crear un objeto XPath y usarlo varias veces
XPath xpathEtiquetaHija= XPath.newInstance("/etiquetaPrincipal/etiquetaHija");
Element etiqueta1 = (Element)xpathEtiquetaHija.selectSingleNode(doc1);
Element etiqueta2 = (Element)xpathEtiquetaHija.selectSingleNode(doc2);
...
Element etiquetaN = (Element)xpathEtiquetaHija.selectSingleNode(docN);

Modificando el XML

Podemos modificar el árbol de nodos del XML parseado. Por ejemplo, vamos a ver lo fácil que es añadir una nueva etiqueta.

// Creamos una nueva etiqueta
Element etiquetaNueva = new Element("etiquetaNueva");
// Añadimos un atributo
etiquetaNueva.setAttribute("atributoNuevo", "Es un nuevo atributo");
// Añadimos contenido
etiquetaNueva.setText("Contenido dentro de la nueva etiqueta");
// La añadimos como hija a una etiqueta ya existente
etiquetaHija.addContent(etiquetaNueva);

Creando XML desde cero

Con JDOM es fácil crear un XML desde cero. Para crear cada objeto del árbol de nodos, tan solo tenemos que usar el operador new.

// Vamos a crear un XML desde cero
// Lo primero es crear el Document
Document docNuevo = new Document();
// Vamos a generar la etiqueta raiz
Element eRaiz = new Element("raiz");
// y la asociamos al document
docNuevo.addContent(eRaiz);

Copiando nodos de un árbol a otro

Con JDOM es muy fácil pasar elementos de un árbol a otro. Aquí no existe el concepto de ámbito de un document como ocurría en DOM. Todos los objetos son susceptibles de ser añadido a cualquier árbol. Lo único que hay que tener en cuenta es que cada objeto sólo puede tener un padre y por tanto si lo que queremos es moverlo de un sitio a otro, habrá que desasociarlo antes del padre. Para ello, la clase Content proporciona el método detach. Es decir, si hacemos detach en un Element, lo que habremos hecho es dejar huérfano ese Element, es decir, ya no tiene padre, por lo que se puede añadir como hijo a cualquier otro nodo.

Si lo que queremos es copiar nodos, usaremos el método clone para generar una copia del mismo y esta copia la podremos añadir a cualquier nodo ya que se genera huérfana.

// Vamos a copiar la etiquetaHija del primer document a este
// Lo primero es crear una copia de etiquetaHija
Element copiaEtiquetaHija = (Element)etiquetaHija.clone();
// Después la colocamos como hija de la etiqueta raiz
eRaiz.addContent(copiaEtiquetaHija);

// Vamos a mover la etiquetaConNamespace a este document
// Primero la desasociamos de su actual padre
etiquetaNamespace.detach();
// Una vez que ya es huerfana la podemos colocar donde queramos
// Por ejemplo, bajo la etiqueta raiz
eRaiz.addContent(etiquetaNamespace);

Una vez ejecutado el ejemplo y si obtenemos la representación en forma de String del XML obtendremos el siguiente resultado:

XML parseado:
<?xml version="1.0" encoding="UTF-8"?>
<etiquetaPrincipal xmlns:xela="http://www.latascadexela.es">
   <etiquetaHija id="1" atributo1="valorAtributo1" atributo2="valorAtributo2">
      Texto dentro de la etiqueta hija
      <etiquetaNueva atributoNuevo="Es un nuevo atributo">Contenido dentro de la nueva etiqueta</etiquetaNueva>
   </etiquetaHija>
   <!-- Comentario -->
</etiquetaPrincipal>


XML nuevo:
<?xml version="1.0" encoding="UTF-8"?>
<raiz>
   <etiquetaHija id="1" atributo1="valorAtributo1" atributo2="valorAtributo2">
      Texto dentro de la etiqueta hija
      <etiquetaNueva atributoNuevo="Es un nuevo atributo">Contenido dentro de la nueva etiqueta</etiquetaNueva>
   </etiquetaHija>
   <xela:etiquetaConNamespace xmlns:xela="http://www.latascadexela.es" descripcion="etiqueta con un namespace" />
</raiz>

Como podemos observar la etiquetaConNamespace se ha eliminado del XML original ya que usamos el método detach para llevarla de un árbol a otro. Esto no ocurre con etiquetaHija ya que previamente fue clonada.

Serializando el XML

La serialización (paso de árbol de nodos a String o bytes) en JDOM es muy similar a la que se puede realizar con DOM. Lo primero que hacemos es especificar el formato de salida del XML, es decir, si queremos identación entre etiquetas, que las etiquetas sin texto tengan etiqueta de cierre o no, que haya un retorno de carro entre etiquetas, que aparezca o no la declaración xml, ....

JDOM ya ofrece tres formatos predefinidos. Son PrettyFormat, RawFormat y CompactFormat. Sus nombres son bastante intiuitivos: bonito, plano y compacto. Pero os remito a la documentación para que sepáis exactamente en qué consiste cada uno.

Una vez especificado el formato, usamos el XMLOutputter y ya podemos serializar tanto Document como Element, como cualquier nodo. Incluso podemos serializar un listado de nodos. La serialización la podemos hacer a String, a OuputStream o a Writer, lo que cubre una amplia gama de posibilidades.

// Vamos a serializar el XML
// Lo primero es obtener el formato de salida
// Partimos del "Formato bonito", aunque también existe el plano y el compacto
Format format = Format.getPrettyFormat();
// Creamos el serializador con el formato deseado
XMLOutputter xmloutputter = new XMLOutputter(format);
// Serializamos el document parseado
String docStr = xmloutputter.outputString(doc);

A continuación dejo el código completo de la clase que he creado como ejemplo:

package es.latascadexela.xml.jdom;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.Namespace;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.jdom.xpath.XPath;

/**
* Clase de ejemplo de procesado de XML mediante JDOM.
*
* @author Xela
*
*/
public class ProcesaXML {

   public static void main(String[] args) {

      try {   
         // Creamos el builder basado en SAX
         SAXBuilder builder = new SAXBuilder();
         // Construimos el arbol DOM a partir del fichero xml
         Document doc = builder.build(new FileInputStream("/ruta_a_fichero/fichero.xml"));

         // Obtenemos la etiqueta raíz
         Element raiz = doc.getRootElement();
         // Recorremos los hijos de la etiqueta raíz
         List hijosRaiz = raiz.getChildren();
         for(Element hijo: hijosRaiz){
            // Obtenemos el nombre y su contenido de tipo texto
            String nombre = hijo.getName();
            String texto = hijo.getValue();
            
            System.out.println("\nEtiqueta: "+nombre+". Texto: "+texto);
            
            // Obtenemos el atributo id si lo hubiera
            String id = hijo.getAttributeValue("id");
            if(id!=null){
               System.out.println("\tId: "+id);
            }
         }
         
         // Obtenemos una etiqueta hija del raiz por nombre
         Element etiquetaHija = raiz.getChild("etiquetaHija");
         System.out.println(etiquetaHija.getName());
         // Incluso podemos obtener directamente el texto de una etiqueta hija
         String texto = raiz.getChildText("etiquetaHija");
         System.out.println(texto);
                  
         // Obtenemos una etiqueta hija del raiz por nombre con Namespaces
         // Primero creamos el objeto Namespace
         Namespace nsXela = Namespace.getNamespace("xela", "http://www.latascadexela.es");
         // Ahora obtenemos el hijo
         Element etiquetaNamespace = raiz.getChild("etiquetaConNamespace", nsXela);
         System.out.println(etiquetaNamespace.getName());
         
         // Buscamos una etiqueta mediante XPath
         Element etiquetaHijaXP = (Element)XPath.selectSingleNode(doc, "/etiquetaPrincipal/etiquetaHija");
         System.out.println(etiquetaHijaXP.getName());
         
         // Buscamos una etiqueta con namespace mediante XPath
         Element etiquetaNamespaceXP = (Element)XPath.selectSingleNode(doc, "/etiquetaPrincipal/xela:etiquetaConNamespace");
         System.out.println(etiquetaNamespaceXP.getName());
         
         // Si hacemos uso muchas veces del mismo XPath sobre varios document
         // es más eficiente crear un objeto XPath y usarlo varias veces
         XPath xpathEtiquetaHija= XPath.newInstance("/etiquetaPrincipal/etiquetaHija");
         Element etiqueta = (Element)xpathEtiquetaHija.selectSingleNode(doc);
         System.out.println(etiqueta.getName());

         // Creamos una nueva etiqueta
         Element etiquetaNueva = new Element("etiquetaNueva");
         // Añadimos un atributo
         etiquetaNueva.setAttribute("atributoNuevo", "Es un nuevo atributo");
         // Añadimos contenido
         etiquetaNueva.setText("Contenido dentro de la nueva etiqueta");
         // La añadimos como hija a una etiqueta ya existente
         etiquetaHija.addContent(etiquetaNueva);
         
         // Vamos a crear un XML desde cero
         // Lo primero es crear el Document
         Document docNuevo = new Document();
         // Vamos a generar la etiqueta raiz
         Element eRaiz = new Element("raiz");
         // y la asociamos al document
         docNuevo.addContent(eRaiz);
         
         // Vamos a copiar la etiquetaHija del primer document a este
         // Lo primero es crear una copia de etiquetaHija
         Element copiaEtiquetaHija = (Element)etiquetaHija.clone();
         // Después la colocamos como hija de la etiqueta raiz
         eRaiz.addContent(copiaEtiquetaHija);
         
         // Vamos a mover la etiquetaConNamespace a este document
         // Primero la desasociamos de su actual padre
         etiquetaNamespace.detach();
         // Una vez que ya es huerfana la podemos colocar donde queramos
         // Por ejemplo, bajo la etiqueta raiz
         eRaiz.addContent(etiquetaNamespace);
         
         
         // Vamos a serializar el XML
         // Lo primero es obtener el formato de salida
         // Partimos del "Formato bonito", aunque también existe el plano y el compacto
         Format format = Format.getPrettyFormat();
         // Creamos el serializador con el formato deseado
         XMLOutputter xmloutputter = new XMLOutputter(format);
         // Serializamos el document parseado
         String docStr = xmloutputter.outputString(doc);
         // Serializamos nuestro nuevo document
         String docNuevoStr = xmloutputter.outputString(docNuevo);
         
         System.out.println("XML parseado:\n"+docStr);
         System.out.println("XML nuevo:\n"+docNuevoStr);
         
      } catch (FileNotFoundException e) {
         e.printStackTrace();
      } catch (JDOMException e) {
         e.printStackTrace();
      } catch (IOException e) {
         e.printStackTrace();
      }

   }

}

Con esto hemos visto lo más importante en cuanto al uso de JDOM. Por supuesto, hay más cosas que no he mencionado y que pueden resultar útil. Simplemente he tratado de dar unas nociones básicas de uso de JDOM para aquellas personas que nunca lo han visto.

Si comparas estos ejemplos con DOM podrás hacerte una idea de lo fácil e intuitivo que es usar JDOM frente a DOM. Si aún usas SAX o DOM en tus proyectos, te animo a adentrarte en el mundo de JDOM. Y si no ves ninguna ventaja, pues nada, sigue con lo que estabas. Desde mi punto de vista las ventajas son muchas y los inconvenientes muy pocos.

En el próximo post veremos ejemplos de uso de XOM, una API parecida a JDOM, que también puede resultar interesante como alternativa a SAX y DOM.