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.

69 comentarios:

Isa dijo...

Hola!!
Muchas gracias por tu artículo, la verdad es que ha sido muy útil y por fin me he aclarado más, y usaré jdom en vez de dom, es más sencillo de utilizar.

Tengo una pregunta,
me gustaría antes de leer el xml, validarlo con un fichero .xsd, esto se puede hacer con jdom?
Por favor si tienen algún ejemplo de esto me vendría muy muy bien.

Muchas gracias

Xela dijo...

Isa, me alegro de que te haya resultado útil. La verdad es que se agradecen este tipo de comentarios.

Lo que comentas no lo he hecho nunca, pero según el javadoc de JDOM, SAXBuilder tiene un constructor con un parámetro booleano que indica si se debe hacer la validación o no.

Supongo que si te creas el SAXBuilder de la siguiente manera te hará la validación de acuerdo al XSD que se indique en el propio XML:


SAXBuilder builder = new SAXBuilder(true);

Ya nos cuentas si te funcionó. ;-)

Isa dijo...

Hola,

no consigo que me valide el xml con el xsd, creo que paso mal el fichero, a ver si me podías ayudar.
Tengo el código:

SAXBuilder builder=new SAXBuilder(true);

Document doc=builder.build("C:\\libro.xml");


Y tengo los dos fichero en C:\

No puedo poner su código, pero hecogido estos ficheros:
http://mat21.etsii.upm.es/mbs/MechXML/ejemplo_de_fichero_xml_schema.htm
con NamespaceSchemaLocation="C:\libro.xsd"

Y cada vez que lo intento validar me pone:

org.jdom.input.JDOMParseException: Error on line 2 of document file:///C:/libro.xml: Document is invalid: no grammar found.
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:501)
at org.jdom.input.SAXBuilder.build(SAXBuilder.java:928)
at ValidarXml.main(ValidarXml.java:14)
Caused by: org.xml.sax.SAXParseException: Document is invalid: no grammar found.

No tengo mucha idea, igual es una tontería, pero no lo veo.

Cualquier ayuda se agradece.

Isa

Xela dijo...

Hola de nuevo Isa,

como ya te dije no lo he hecho nunca, pero por la excepción que te da parece que no estás indicando bien la ruta al xsd.

Debes tener algo parecido a esto:

<Libro xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="C:\libro.xsd" precio="20">

Prueba a poner C:\\libro.xsd o simplemente libro.xsd. También puedes probar a ponerlo en algún servidor apache de forma que la ruta te quede algo parecido a http://servidor/libro.xsd.

No sé. La verdad es que no te puedo ayudar mucho más. Lo siento.

A ver si alguien que lo haya hecho te echa una mano.

Saludos

Fran dijo...

Hola Xela, la duda que te comentaba tiene que ver con la subseccion "Parseando un XML". Ahi pones el codigo necesario para generar el arbol de nodos a partir del XML utilizando SAXBuilder. Podrias poner como se haria esto sin usar SAX, por favor? Es que dado que yo voy a modificar el XML no se si pasara algo si creo el arbol de nodos con SAXBuilder...
Muchas gracias!

Xela dijo...

No te preocupes por ello, Fran. Usa SAXBuilder. Esto es lo a lo que me refería en el otro post cuando te decía que si tu fichero era grande sería conveniente que usases SAX para generar el árbol de nodos. Sólo se usa SAX para generar el árbol de nodos, pero después puedes trabajar con el árbol DOM y modificarlo sin problemas.

;-)

Fran dijo...

Muchas gracias Xela! Esto todavia me parece una montaña, pero a ver si poco a poco...
:)

Fran dijo...

Hola Xela,
Una duda un poco tonta... Cuando pones
System.out.println("\nEtiqueta: "+nombre+". Texto: "+texto); Por que pones el \n??
O en este caso:
System.out.println("\tId: "+id); Por que pones el \t??
Donde puedo conseguir mas informacion sobre lo que parecen ser unos modificadores en println?
Muchas gracias!

Xela dijo...

Fran,

son caracteres especiales. \n es el caracter nueva línea y \t es el tabulador. Simplemente es por claridad a la hora de mostrar el resultado de mi ejemplo.

Debes saber también, que println inserta un \n al final de lo que escribas, es decir, imprime lo que le pases por parámetros y un salto de línea. Si no quisieras eso debes usar la función print.

;-)

Fran dijo...

Muchas gracias Xela!
Ya siento ser tan pesadico...

Xela dijo...

No te preocupes, Fran. Uno de los objetivos de este blog es ayudar a la comunidad. Probablemente tus dudas sean compartidas por otras personas por lo que exponiéndolas también ayudas a ellos a resolverlas.

Pregunta sin problema. Te ayudaré en lo que esté en mi mano.

Gracias a tí por preguntar. ;-)

Fran dijo...

Hola Xela,
Me ha surgido un problemilla, te cuento. He seguido los pasos de este post para ver si podia hacer funcionar el ejemplo en NetBeans 6.1. Sin embargo, cuando le doy a correr el ejemplo no me funciona y me dice que hay un error de configuracion de Ant:

ERROR - Ant is misconfigured and cannot be run.
java.lang.ClassNotFoundException: org.apache.tools.ant.input.InputHandler
at java.net.URLClassLoader$1.run(URLClassLoader.java:200)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(URLClassLoader.java:188)
at org.apache.tools.ant.module.bridge.AuxClassLoader.findClass(AuxClassLoader.java:85)
at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
at java.lang.ClassLoader.loadClass(ClassLoader.java:251)
at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:247)
at org.apache.tools.ant.module.bridge.AntBridge.createAntInstance(AntBridge.java:283)
at org.apache.tools.ant.module.bridge.AntBridge.getAntInstance(AntBridge.java:264)
at org.apache.tools.ant.module.bridge.AntBridge.getInterface(AntBridge.java:253)
at org.apache.tools.ant.module.run.TargetExecutor.run(TargetExecutor.java:460)
at org.netbeans.core.execution.RunClassThread.run(RunClassThread.java:151)

A mi esto de configurar librerias y tal siempre me ha parecido un poco lioso asi que en un primer momento lo que hice fue "seguir la ley del minimo esfuerzo" que mencionas en el post. Pero al ver que me aparecia este error lo que he hecho es seguir las instrucciones del README.txt de JDOM que me baje y descomprimi. En mi caso ha consistido en:

C:\> set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_07

Despues ejecutar el build.bat (que me ha creado la libreria jdom.jar dentro de la carpeta "build").

Y en principio no pone en el README.txt que haya que hacer mas cosas... sin embargo al ejecutar de nuevo el programa me sigue apareciendo exactamente el mismo error (y eso que he reiniciado el NetBeans). Que puede estar pasando? Alguna idea? Muchas gracias!

Fran.

Fran dijo...

Vale Xela, problema solucionado... Lo he encontrado en:

http://saloon.javaranch.com/cgi-bin/ubb/ultimatebb.cgi?ubb=get_topic&f=12&t=006088

Por si a alguien le puede servir...

Fran dijo...

Hola Xela, te escribo al borde de la desesperacion...jeje. Te comento mi duda, que quizas tenga mas que ver con Java propiamente que con JDOM, pero hay relacion en todo caso.
Tengo un XML enorme del que, por el momento, solo quiero centrarme en una parte que tiene el siguiente aspecto:

{raiz}
...
{modulodereglas>}
{grupodelasreglas}...{grupodelasreglas}
{regla nombre="..." otroatributo="..."}
{condicionparaaplicarregla/}
{loquehacelaregla blabla="..."/}
{ficheroqueusalaregla}nombrefichero{fich../}
{parametros nombre="..." tipodevalor="..."}
{valor tipo="..." valorreal="..."/}
{/parametros}
{/regla}
{regla ...}
...
{/regla}
...
{regla}
...
{/regla}
{/modulodereglas}

Mi duda es la siguiente: hasta ahora, con la instruccion: List{Element} ruleModules = raiz.getChildren("modulodereglas"); he conseguido una lista con todos los hijos de raiz que son "modulodereglas". Lo que me gustaria ahora es, para cada uno de esos "modulodereglas" crearme una lista con todas las "regla" que haya dentro. No se si para esto lo mejor es utilizar una lista o no (y que tipo de lista). Mi objetivo es tener toda la informacion guardada en memoria de manera que luego pueda construirme una interfaz grafica con toda la informacion, y dicha interfaz grafica pueda modificar el valor de lo contenido en las "regla". Como soy muy principiante en Java te pediria si por favor podrias ayudarme a construir ese "bucle" que recursivamente me vaya guardando toda la informacion para que luego yo pueda accederla con indices en listas o como pueda resultar mas comodo. Igual te he liado mucho con esta pregunta, pero tenía que intentarlo porque estoy atascadisimo! Muchas gracias,
Fran.

Xela dijo...

Hola Fran,

efectivamente tu duda radica más en una falta de base en Java que en JDOM. En este articulo puedes ver cómo recorrer un XML con JDOM (lo más básico es usa getChildren tantas veces como necesites, aunque es más cómodo usar XPath). Por lo que comentas, entiendo que tu duda radica en cómo almacenarlo en memoria. Creo que deberías echar un vistazo al paquete java.util de Java. Probablemente necesites hacer uso de listas (List/ArrayList), mapas (Map/HashMap) o conjuntos (Set/TreeSet).

Lo que yo haría sería hacerme una clase Regla que contenga toda la información necesaria de una regla. Vas recorriendo el xml y por cada regla te creas un objeto de tipo Regla. Una vez creado el objeto Regla y almacenada toda la información en él, lo metes en una colección(Collection) (lista, un conjunto o un mapa). Cuando quieras acceder a las reglas accedes a tu colección y recorres las reglas que convenga.

Puedes modificar los objetos Regla cuando quieras. Una vez llegue la hora de guardar la información de las reglas, recorres tu colección de reglas y generas de nuevo el fichero XML con JDOM y listo.

Puede venirte bien echarle un vistazo a la documentación oficial de Java sobre Collections

Espero que haberte aclarado algo.

Fran dijo...

Muchas gracias Xela, voy a seguir currando con los consejos que me has dado!

gauss dijo...

xala:

Gracias por los artículos en tu blog! Al menos las que he leído, las que refieren a XML/Java me han resultado un clarísimo resumen tanto de las tecnologías como de su aplicación conjunta. Es precisamente lo que me estaba haciendoa falta para tener una idea global a la vaz que práctica de imnipresente XML, su tratamiento, sus opciones, su manejo programático.

Nuavamente: gracias!

PD: ¿Has trabajado con JBoss, en particulat con JBossESB? La necesidad que enfrento es transformar mensajes HL7 (XML para el sector médico) y la idea es manejar estos documentos entre las entidades que saben generar y leer HL7 y las como que no, transformándolos mediante las características del ESB.
Bueno: un abrazo y seguiré tu blog con atención!

Xela dijo...

Muchas gracias, gauss. No sabes cuánto se agradecen este tipo de comentarios. Estoy preparando el último post sobre Java y XML. En este caso trata de XOM. Es bastante parecido a JDOM y puede resultar otra alternativa al tratamiento de XML con Java. Espero tenerlo pronto.

En cuanto a JBossESB, lo siento. No he tocado nada de eso.

Saludos.

Cris dijo...

Hola a tod@s!!

Antes de nada muchas gracias por el post Xela, que me esta sirviendo de gran ayuda.
Quiza llegue un poco tarde para responder a Isa, pero bueno puede que os valga a los demas.
He encontrado un código que valida xml contra un xsd y parece que funciona. Lo podeis ver en http://www.lawebdelprogramador.com/news/mostrar_new.php?id=124&texto=XML&n1=452343&n2=1&n3=0&n4=0&n5=0&n6=0&n7=0&n8=0&n9=0&n0=0

Salu2.

Fran dijo...

Hola Xela, te comento un problemilla que estoy teniendo.

Tengo varias estructuras en un XML que se me repiten y las estoy guardando en listas (enlazadas) de objetos, pq me interesa tenerlas en memoria.

El problema viene con que una de las etiquetas a veces esta vacia y a veces tiene un atributo. Lo que me sucede es que uso las siguientes instrucciones:

Element ruleconditioN = ruleitem.getChild("condition");
String rulecondition = ruleconditioN.getAttributeValue("type", "null");

Entonces cuando la etiqueta esta vacia espero obtener null en el String rulecondition. Sin embargo, para mi sorpresa, esto no sucede. Se me guarda en dicho String el valor de el atributo que aparece en otro de los bloques.

En mi estructura, despues de varios bloques con esa etiqueta vacia, el primer bloque que tiene esa etiqueta con atributo pongamos que tiene valor=1. Pues bien, en todos los bloques que tengo esa etiqueta vacia, el valor que me guarda en lugar de ser null es 1.

Luego hay bloques que tienen esa etiqueta con valor 2, 3 o lo que sea, y esos me los guarda bien.

Alguna idea para conseguir que me ponga a null cuando la etiqueta esta vacia?

Muchas gracias!!

Xela dijo...

Pues no sé Fran, algo debe haber mal en tu código. Puede que estés accediendo siempre al mismo hijo de ruleitem, ya que con getChild() obtienes el primer hijo. Si tienes más de un hijo siempre obtienes el primero.

Por otro lado, tienes que distinguir entre el valor null que significa que el objeto es nulo, y el valor "null" que significa la cadena "null" que ES un objeto igual que "Fran" por ejemplo. También existe el objeto cadena vacía "", que también es un objeto y por supuesto es distinto de null.

Por último, creo que getAttributeValue(String name) ya te devuelve null si el atributo no existe. Mira el javadoc de JDOM

En fin, te toca pelearte con tu código y ponerte a depurar a ver donde está el error.

¡¡¡Suerte!!!

Anónimo dijo...

Buenas, lo primero felicitarte por el gran post que has creado.
Mi duda es la siguiente. Me creo un xml, con formato bonito, y al root le meto una serie de atributos y de namespaces. Mi problema viene que cuando ha creado el xml con el serializador XMLOutputter como dices me aparece todos los atributos y namespaces en un sola linea, que es la linea del root, y que decirte que es gigante. Me gustaría meter un salto de linea entre los xmlns, y que los atributos estubieran en una sola linea

¿Cómo podría hacerlo? Un saludo!

Xela dijo...

Muchas gracias. XD

He estado mirando la api de jdom y no he visto ningún método en la clase Format que permita hacer lo que cuentas. Lo único que se me ocurre es que te generes tu propio XMLOutputter. Lo único que tienes que hacer es generar una clase que herede de XMLOutputter y sobreescribir el método

protected void printAttributes(Writer out, List attributes, Element parent, NamespaceStack namespaces)

Si te descargas el código fuente puedes ver que recorre los atributos y los va imprimiendo. Tan sólo tendrás que añadir el separador de línea después de imprimir cada atributo (línea en negrita). Por supuesto, puedes añadir un control para que si es el último parámetro no lo añada, pero eso ya te lo dejo a tí. ;-)

   /**
    * This will handle printing of a <code>{@link Attribute}</code> list.
    *
    * @param attributes <code>List</code> of Attribute objcts
    * @param out <code>Writer</code> to use
    */
   protected void printAttributes(Writer out, List attributes, Element parent,
                         NamespaceStack namespaces)
                throws IOException {

      // I do not yet handle the case where the same prefix maps to
      // two different URIs. For attributes on the same element
      // this is illegal; but as yet we don't throw an exception
      // if someone tries to do this
      // Set prefixes = new HashSet();
      for (int i = 0; i < attributes.size(); i++) {
         Attribute attribute = (Attribute) attributes.get(i);
         Namespace ns = attribute.getNamespace();
         if ((ns != Namespace.NO_NAMESPACE) &&
            (ns != Namespace.XML_NAMESPACE)) {
               printNamespace(out, ns, namespaces);
         }

         out.write(" ");
         printQualifiedName(out, attribute);
         out.write("=");

         out.write("\"");
         out.write(escapeAttributeEntities(attribute.getValue()));
         out.write("\"");
         out.write(currentFormat.getLineSeparator());
      }
   }

Después tan sólo tienes que usar tu XMLOutputterGeneradoPorTi y listo.

Espero que esto te ayude. ;-)

Angel dijo...

Muchas gracias por una respuesta tan rápida, voy a probarlo a ver, parece que tiene muy buena pinta lo que has planteado. Ya te cuento el resultado.

Otra cosa que te quería comentar es que mirando XMLs me he dado cuenta que a lo mejor lo de poner el salto de línea en los xmlns y luego no ponerlo en el resto de atributos, no viene condicionado por un formato, sino por un número máximo de caracteres por línea.

Me explico, como los xmlns son normalmente direcciones no tienen ningún espacio entre palabras, son Strings muy largos, que cuando llegan al final de la línea el siguiente xmlns no cabe, por lo que salta de línea, y como los atributos son cortitos (normalmente) pues si que caben varios en una misma línea.

Me he dado cuenta de esto, mirando un xml he visto que para el texto de una etiqueta, cuando llegaba a un cierto máximo saltaba automáticamente de línea, y que cuando yo genero un elemento con JDOM y le pongo el setText, a mi me escribe todo lo larga que quiera la línea.

¿Tu sabrías como se puede acotar el número máximo de caracteres por línea o algo parecido?. Siento tantas preguntas, pero veo que controlas mucho y no puedo pasar la oportunidad de preguntártelo, jeje. ¡Un saludo y gracias de antemano!.

Bejaranor dijo...

Perfecto. Funciona a la perfección y es muy útil.

Muchas gracias por compartirlo con todos nosotros.

Un saludo

Fran dijo...

Hola Xela, mi duda es la siguiente.

Estoy guardando en memoria todo el contenido de un fichero XML. Me he creado unas cuantas clases correspondientes a diferentes etiquetas y mediante instrucciones como getAttributeValue(), etc... voy guardando todo sin problemas.

Si ademas de la informacion quisiera tambien guardar "comentarios" que hay escritos en el XML para facilitar su comprension, sabes que instruccion tendria que usar?

Muchas gracias!
Fran.

Xela dijo...

Antetodo, gracias a todos por vuestros comentarios. Este post se está enriqueciendo mucho con ellos. Espero que sean muchos más. ;-)

Os respondo por partes:

Bejaranor, me alegra de que te hayas animado a probarlo. Os animo también a vosotros a poner vuestras mejoras o truquillos que puedan resultar de interés para el resto.

Angel, no he visto nada en la API de JDOM para hacer lo que dices. De todas formas, como digo en el comentario anterior, lo que he puesto es sólo un ejemplo. Puedes complicarlo tanto como quieras. Quizás a ti te interese fijar un número de caracteres máximo por línea. Vas controlando cuántos caracteres se van escribiendo en el método y en cada iteración del bucle preguntas si se ha llegado a ese máximo. En caso afirmativo metes el separador de línea y en caso negativo no. De esta forma, los atributos pequeñitos entrarán varios en una línea y los grandes no.

Fran, la API de JDOM permite leer los comentarios sin problemas. Para ello tienes que acceder al element padre del comentario y usar el método getContent(). Esto te devuelve una lista con todos los hijos, ya sean texto, comentarios, otros elementos, ... En este caso, al recorrer la lista de nodos tendrás que ir discriminando qué tipo de nodo es. Por ejemplo:

List nodos = element.getContent();
for(int i=0;i<nodos.size();i++){
   Content nodo=nodos.get(i);
   if(nodo instanceof Element){
      //Es un Element
   }else if(nodo instanceof Comment){
      //Es un comentario
   }else if(nodo instanceof Text){
      //Es texto
   }
}

Si únicamente quieres leer los comentarios puedes usar el método getContent(Filter filter), al cual se le pasa un filtro para que te dé sólo ciertos tipos de hijos. Por ejemplo, en el caso de que quieras leer únicamente los comentarios sería así.

Filter filter = new ContentFilter(ContentFilter.COMMENT);
List comentarios = x.getContent(filter);

Puedes obtener más información de ContentFilter aquí.

Espero haber resuelto vuestras dudas.

Saludos

;-)

Angel dijo...

Como siempre perfecto, muchas gracias otra vez!

Fran dijo...

Muchas gracias Xela!!
:)

Martin dijo...

Hola, excelente articulo, para iniciarse. Sin embargo a mi no me funciona el ejemplo que expones. Copie tal y como lo tienes escrito.

Lo probe con JBuilder añadiendo los *.jar que trae JDom.

me da el siguiente mensaje

"class, interface, or enum expected"

Graciar por compartir

Xela dijo...

Martin, tienes que añadir todos los jars que hay en el directorio lib del tar.gz de jdom y el jar de jdom que está en el directorio build. Con esto debería funcionar sin problemas.

Saludos

Anónimo dijo...

Hola a todo el mundo

estoy en un proyecto y estoy realizando la creacion de un XML con JDOM. Todo parece ir bien pero a la hora de la creación del fichero, me lo crea vacio, sin añadir los distintos elementos. He estado evisandolo, y yo no puedo encontrar el error.
Os dejo el codigo para ver si podeis ayudarme.

import java.io.*;
import java.util.Date;

import org.jdom.*;
import org.jdom.output.*;



public class CreateXml {

public void generar (String destino,String notificacion) throws Exception{
System.out.println("createXml() Metodo que crea el xml ");
Element item = null;

//Creamos un elemento root
Element root=new Element("DOCUMENT");


item = new Element("REMITENTE").setText("D. Fernando Sa");
root.addContent(item);

item = new Element("FECHA_ACTUAL").setText(new Date().toString());
root.addContent(item);


Document doc=new Document(root);
System.out.println("root "+root.getContentSize());
try{
XMLOutputter out=new XMLOutputter(Format.getPrettyFormat());
System.out.println("archivo "+destino);
FileOutputStream file=new FileOutputStream(destino);
out.output(doc,file);
file.flush();
file.close();
out.output(doc,System.out);
}catch(Exception e){
e.printStackTrace();
}

}

}

Gracias y un saludo

Angel dijo...

Hola Xela,
trabajando con JDOM me he dado cuenta de algo grave, me refiero al uso de acentos y ñ.

A la hora de crear un xml no hay problema, puede meterle por ejemplo a los atributos de un elemento alguna palabra con ñ o con acentos.

Ahora bien, cuando ese mismo fichero generado intento parsearlo no puedo, me da un error y deja de parsear. Quitando de dentro del xml todos los acentos y eñes lo lee perfectamente.

¿Tienes alguna idea de como puedo hacer el parseo?¿O alguna manera de guardar el xml para que luego se pueda leer?

Un saludo.

Xela dijo...

Angel, bienvenido al maravilloso mundo de los encodings. Tu problema es bien conocido. Estás guardando el fichero en una codificación y tienes en la etiqueta <?xml ... encoding="UTF-8"?> otra codificación distinta.

Esta etiqueta sirve para indicar en que codificación está en el xml. En este caso es UTF-8. Sin embargo, si dices que el xml está en UTF-8 y después el fichero no lo está realmente, pues la cosa va mal y por eso falla.

Probablemente sea cosa del editor que uses para crear/modificar el fichero. Asegurate de que el encoding del fichero es el mismo que el que pones en la etiqueta del xml y no tendrás problema.

Si usas linux puedes ver el encoding del fichero con el comando file. Por ejemplo:

bash$ file nombre_fichero.xml

Saludos

Angel dijo...

Gracias Xela por tu respuesta, al final encontré la solución. Yo guardaba el xml con la codificación UTF-8, pero ahora la guardo con el encoding ISO-8859-1. Esto me ha solucionado la lectura del documento que contiene tildes, signos de exclamación e interrogación y nuestra tan querida Ñ...

Muchas gracias por responder, y agradezco la labor que haces con este blog.

Un saludo.

Xela dijo...

Angel, me alegro que lo hayas solucionado. Si guardas el fichero en ISO-8859-1 y funciona debe ser porque la primera línea de tu xml debe sera algo parecido a <?xml version="1.0" encoding="ISO-8859-1"?>. La codificación del fichero debe coincidir con la de esta etiqueta para que todo vaya ok.

¿Puedes confirmar esto?

Por cierto, gracias a todos vosotros por hacer que este blog sea participativo. El placer es mío al ayudar en lo que pueda. ;-)

Angel dijo...

Hola Xela, te cuento todo lo que pasaba.
Necesitaba generar mediante java documentos xml. Para crearlos lo hacía de esta forma:

String strXMLResultado = new String()

//Formato de salida
Format format = Format.getPrettyFormat()
format.setEncoding("UTF-8")

// Objeto XML
Document doc = null

Luego el root y todos los nodos que irían dentro del XML.

//Por último
doc = new Document(root)
strXMLResultado = new XMLOutputter(format).outputString(doc)

Entonces esta cadena la guardaba mediante la clase FileWriter en fichero con extension XML.

Cuando abría el archivo creado, pues era un XML normal y corriente donde el encoding en la parte superior era:
..version="1.0" encoding="UTF-8"

Ahora bien, mi problema era que ese XML, cuando lo tenía que abrir con otra aplicación web que estoy desarrollando por otro lado usaba:

SAXBuilder builder=new SAXBuilder()

// Cargaba el arbol de nodos
Document doc=builder.build(new FileInputStream(rutaXML))

Y era en ese momento en que me daba un error si el XML que intentaba cargar tenía cualquier tipo de caracteres raros, tildes, eñes, etc. Como esto siempre va entre try-catch pues se capturaba la excepción y a otra cosa, pero no conseguía cargar el árbol de nodos.

Para solucionar la carga de los xml anteriormente generados tuve que cambiar la linea de código:
format.setEncoding("ISO-8859-1");

Esto es lo que me lo resolvío, nosé muy bien porque, ya que pensaba que el UTF-8 incluye los caracteres que me daban problemas.

Espero que esto sirva.

Un saludo!

Xela dijo...

Muchas gracias por responder Angel.

Te cuento cual fue tu problema realmente. Pero antes debo aclarar cómo se guardan los ficheros. Cuando tu guardas un fichero de texto, no se guarda el texto como tal, es decir, no se guardan letras. Lo que se guardan es la representación numérica de los caracteres. En concreto lo que se guardan son los bytes de esos números. Un carácter puede estar representado por uno o más bytes. La traducción de bytes (números) a letras y viceversa es precisamente la codificación, por ejemplo ISO-8859-1 y UTF-8. Por ejemplo, el carácter "A" se representa numéricamente como un "65" y este número coincide tanto en ISO-8859-1 como en UTF-8.

Por supuesto que existen tildes y ñ en UTF-8, lo que pasa es que la representación en bytes de las tildes, ñ y otros caracteres más "raros" no es la misma en ISO-8859-1 que en UTF-8. Por eso si intentas leer la representación en text de una serie de bytes aplicando la codificación equivocada, normalmente las letras "normales" salen bien, pero no los caracteres "raros".


Aplicando lo dicho a tu caso con el XML, podemos decir lo siguiente. Como ya comenté los ficheros XML son autocontenidos, es decir, en el propio contenido del XML se especifica cuál es la codificación del fichero. En tu caso, aunque estabas diciendo en la etiqueta del XML version=".... que el XML estaba en UTF-8, el fichero XML lo estabas generando en ISO-8859-1. Supongo que es la configuración de tu sistema y lo aplica por defecto al generar ficheros. Entonces, estabas generando un fichero ISO-8859-1 mientras que estabas dicendo dentro del XML que era UTF-8. Al leer el fichero, el parseador leía que era UTF-8 en la etiqueta XML version="... en lugar de ISO-8859-1 e interpretaba los bytes de la forma que no era. Como ya he dicho en ISO-8859-1 y en UTF-8 las letras "normales" tienen la misma codificación en bytes, pero las tildes y ñ y caracteres más raros, ya no. Por tanto se estaban interpretando mal.

Al hacer el format.setEncoding("ISO-8859-1") lo que estás es haciendo que el XML version="... ponga la codificación correcta en la que está escrito el fichero y por tanto ya no tienes problemas al leer.

Si en lugar de hacer el format.setEncoding("ISO-8859-1") hubieras forzado de alguna forma que se escriban bytes de acuerdo a la codificación UTF-8 también te hubiera funcionado. Supongo que se haría mediante una configuración del FileWriter que usas para escribir el fichero o usando un OutputStream.

Espero que con esta parrafada hayas entendido el por qué tu solución sirve. ;-)

Puede que alguno diga: "si ya se ha solucionado, para qué darle más vueltas". A mí por lo menos, no me basta con solucionar las cosas sino que me gusta saber cómo y por qué se solucionan. :-p

Saludos.

Angel dijo...

De nuevo muchas gracias por explicar de manera clara el por que de las cosas. Gracias a leer estas "parrafadas" podemos entender un poco mejor el aposinante mundo de los encodings...

Un saludo, y otra vez te agradezco tu tiempo.

Mayra Carolina dijo...

a mi me da error al importar las librerias
import org.jdom.*;
import org.jdom.input.*;
import org.jdom.output.*;

Xela dijo...

Mayra Carolina,

tu problema debe ser que te falta incluir los jars de jdom. Te copio la parte del post donde dice cómo hacerlo.


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.



Saludos

Anónimo dijo...

Hola,

necesitaria generar xml desde un xsd... como podria hacer eso con jdom?

Xela dijo...

Un XSD al fin y al cabo no deja de ser un XML. Tendrás que parsear el XSD(igual que se detalla en este post), recorrerlo e ir generando un nuevo XML según te vaya indicando el XSD.

;-)

Silvernine dijo...

Te agradezco inmensamente por la explicación y los ejemplos llevo muy poco trabajndo con java y esto era lo que realmente necesitaba y

no se si es demasiada ignorancia pero mi proyecto no genera el directorio lib.

debo crearlo?
el Jdom.jar también debe ir en la carpeta lib?

Xela dijo...

Silvernine, si tu proyecto va en un contenedor de aplicaciones como Tomcat, tendrás que meter el jdom.jar en una carpeta llamada WEB-INF/lib. Si no, pues donde vayan los otros jars. ;-)

Roberto dijo...

Hola Xela, primero de todo felicitarte por tu post, me parece muy útil.

Te expongo mi problema que me están dando muchos quebraderos de cabeza a ver si me puedes dar alguna solución (el problema es sobre encodings).

Resulta que estoy modificando una aplicación de videoclub que en un xml guarda toda la información de las películas, el encoding del xml es UTF-8 así mismo el fichero xml está guardado con codificación UTF-8.

La aplicación usa SALT para reconocimiento de voz que se realiza con un intérprete loquendo, para no volverte loco decirte que es una aplicación para descodificadores de TV así que por una parte puedes moverte por el menú de películas con la voz o con el mando a distancia.

Bien, por otro lado hay que parsear el XML esto se realiza con JDOM en un fichero llamado VideoclubParse.java.

El problema yo creo que es de encondings porque si meto acentos al XML el intérprete loquendo no reproduce el texto, cuando está sin acentos el texto lo lee bien pero lo pronuncia sin acentos (esto no queda bien de cara al usuario final).

Así que por lo que veo es un problema de encoding a la hora de parsear el XML, de momento no veo una solución al problema, no sé si podrías darme tú alguna.

Muchas gracias por la ayuda y perdón por el rollo de texto que he metido xD.

Saludos.

Xela dijo...

Roberto,

creo que lo primero que debes hacer aislar y detectar el problema. Intenta meter acentos a mano al SALT, a ver si los interpreta bien. E intenta sacar por algún log o algún sítio información con tildes extraída del XML. Te lo digo porque según me comentas no tienes muy claro si el problema es del parseo del XML o del SALT.

Por otro lado, también puede ser que aunque lo leas bien desde el XML, la entrada que le pasas al SALT puede que esté esperando otra codificación distinta. El problema con los encodings suelen estar en el paso de bytes a caracteres y viceversa. Si haces algún tratamiento en que tengas que hacer ese paso, ahí tienes muchas posibilidades de tener el problema.

Siento no poder ayudarte más, pero es complicado sin tener la aplicación delante.

Fran dijo...

Hola Xela,

Te escribo porque he hecho un programa con Eclipse y Java usando JDOM para parsear un XML y tal.
El tema es que en Eclipse cuando tenia el proyecto, ademas de las carpetas con los archivos .java, tenia importadas al proyecto unos cuantos .jar relativos a JDOM que necesitaba para que me corriera el programa.
Sin embargo, a la hora de crearme el fichero .jar con los .java de mi proyecto, no puedo incluir esos .jar relativos a JDOM y por tanto al ejecutar mi .jar no me funciona.

Obtengo algo como:
Exception in thread "main" java.lang.NoClassDefFoundError: org/jdom/JDOMException

Como podria de alguna manera incluir los .jar del JDOM para que mi .jar funcione??

Muchas gracias!!
Fran.

Anónimo dijo...

Hola Xela!
Mira yo no se programar, hay una amiga que mas o menos sabe programar, me esta ayudando a modificar un template, pero tengo el error que comentaban hace tiempo, no me salen letras con acentos ni la letra ñ, pero leyendo me he dado cuenta de que se suponia que teniendo lo siguiente tendria que funcionar:
en la primera linea encoding ISO-8859-1
no puedo ponerlo bien porq no me deja subir el comentario, pero igual que lo escribiste el 3 de marzo. Y siguen sin aparecer la ñ y los acentos. Espero me puedas ayudar y decirme en que archivo puedo cambiar algo para que no me siga dando ese error, o que puedo hacar. Gracias y te dejo mi correo swatch1812@hotmail.com por si gustas agregarme y asi poder pasarte un print screen para que veas los archivos que tengo pues solo tengo 3 extensiones xml donde modificarlos con bloc de notas y ya todos tienen iso-8859-1.

SAludos!

Atte: kristian f.

luis dijo...

hola... tengo un problema ... tengo una aplicacion en la que tengo que editar una sola etiqueta del XML y resulta que cuando estoy en internet me funciona muy bien pero cuando me desconecto deja de funcionar... trabajo con JRXML (jasperReport)... pero no lo edito con JDOM... me puedes ayudar

dontcare dijo...

have you looked at vtd-xml, which is the latest and more advanced/powerful XML Processing API

vtd-xml

srarcangel dijo...
Este comentario ha sido eliminado por el autor.
srarcangel dijo...
Este comentario ha sido eliminado por el autor.
srarcangel dijo...

Suprimir comentario de: La Tasca de Xela

Blogger srarcangel dijo...

Hola, he estado siguiendo el ejemplo para usar el jdom, pero mi xml, es un poco distinto y me peta cuando intentar crear en memoria el arbol de contenidos del fichero xml...

Document doc=builder.build(origen);
*origen es la ruta del fichero.

La diferencia en el xml del ejemplo con el mio es, que las etiquetas son de este tipo:
abre metadata
abre dc:title cierra dc:title
......
cierra metadata
La saslida del copmpilador es la siguiente: The prefix "dc" for element "dc:title" is not bound.

Xela dijo...

¿Tienes definido el namespace para dc en el xml?

Mayra Carolina dijo...

miren aqui hay un ejemplo de xml http://javafacil.netii.net/mas.php

oscar canencia dijo...
Este comentario ha sido eliminado por el autor.
oscar canencia dijo...

eso es debido a que no tienes bien el xml schmetic, si por ejemplo tienes:
<-xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/emisor" xmlns:tns="http://www.example.org/emisor" elementFormDefault="qualified">
<-xsd:element name="emisor">
<-/xsd:element>
<-/xsd:schema>
y creo que lo que te falta es la parte:
<xsd:schema xmlns:xsd="
del principio.
AVISO: el ejemplo es sin el guion "-".

Eloy-caballo dijo...
Este comentario ha sido eliminado por el autor.
PoPeWeB dijo...
Este comentario ha sido eliminado por el autor.
PoPeWeB dijo...

Suprimir comentario de: La Tasca de Xela

Blogger PoPeWeB dijo...

Hola Xela , magnifico articulo me ha sido de gran ayuda, yo habia hecho varias cosas con Sax , pero estaba buscando la forma de generar un xml , el problema que me he encontrado fue con los namespace ,necesito escribir lo siguiente.

asset xsi:noNamespaceSchemaLocation="app-type-schema_3pp_v2.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

NOTA:faltan los simbolos de apertura y cierre de la etiqueta porque no me visualiza el codigo con ellas puestas.
siendo asset mi elemento root , pero no consigo recrearlo tal cual, confio en que puedas ayudarme . Un saludo y Gracias

FERNANDO dijo...

Que tal!!

Es posible agregar elementos en tiempo de ejecucion?? quiero decir tengo el siguiente archivo creado con JDOM:


root
/root


y quisiera agregar algo asi:

root /* etiqueta raiz */
tagchild /* etiqueta hija */
child /* etiqueta con contenido */
Algo de texto /*texto dentro etiqueta */
/child /* fin etiqueta con contenido */
/tagchild /* fin etiqueta hija */
/root /* fin etiqueta raiz */

Mirna Cambero dijo...

buen post! Gracias

Richard Chamba dijo...

Hola Xela... todo bien..??
tengo un problema en la sección parseando un XML, en esta linea
Document doc = dombuilder.build(documentDOM);

me da un error de "no suitable method found for build(org.jdom.Document)

no se mi me podrias ayudar en esa parte por favor gracias y saludos.

marcos esperanza dijo...

Hola que tal, excelente el aporte. Tengo una duda tengo el código que guarda un cadena a un archivo .xml , todo bien hasta ahí desde el netbeans lo hace sin problemas. Pero desde el jar cambia la codificación de UTF-8 a ANSI, alguna sugerencia de lo que puede estar pasando ?? Gracias.

marcos esperanza dijo...

Bueno en primera el .jar se estaba ejecutando con un charset distinto. Para solucionarlo utilice esto:

XMLOutputter xmlOutput = new XMLOutputter();
Format format = Format.getPrettyFormat();
format.setEncoding("UTF-8");
xmlOutput.setFormat(format);

Y en la parte donde se guarda el archivo .xml, en vez de usar un FileWriter utilice un FileOutputStream quedando de la siguiente manera:

FileOutputStream out = new FileOutputStream(ruta);
xmlOutput.output(doc, out);

Lo comento, por si alguien después presenta el mismo problema. Saludos.

sistemasinfo dijo...

hola, puedes ayudarme..
use el codigo de leer los datos del Xml, es decir los datos guardados en mi archivo, quiero mostrarlos, y reutilizarlos, por ejemplo tengo guardado edades, y quiero saber quien es el mayor y menor..
y al mostrarlo en panalla dice Null, tengo 5 datos en mi archivo, y 5 veces dice Null.
te djo mi correo, navarrete2013@gmail.com

diego lopez dijo...

hola como estan me podrian ayudar
deseo saber como hago para modificar
varios nodos padre y adicional como
hago para buscar el directorio xml
sin saber su nombre es decir
siempre me van a llegar diferentes
archivos xml pero desconocere su
nombre

Gerardo Ramirez dijo...

Hola buen dia, espero me puedan ayudar, en mi codigo java leo el xml y lo procesa muy bien pero me llego una prueba con una etiqueta faltante, en el string que la recibe se almacena un null pero tambien luego luego me manda la excepcion nullPointerException con lo cual se pasa a leer el siguiente xml y el que tiene la falta de etiqueta ya no lo puedo procesar, les envio la parte del codigo, saludos.

String FechaNacimiento =null;
FechaNacimiento= Poliza.getChildTextTrim("FechaNacimiento");
System.out.println("F NCIMIENTO " + FechaNacimiento);
if(FechaNacimiento.equals("") || FechaNacimiento==null){
bandfnac=false;
}

La etiqueta FechaNacimiento es la que no llega en el xml