1

Estoy intentando mostrar un archivo XML por pantalla a través de DOM, pero tras realizar la conversión del susodicho archivo, al querer guardar los datos de cada nodo en un array me aparece el error:

    Exception in thread "main" java.lang.NullPointerException
    at gestionardom.Metodos.procesarLibro(Metodos.java:103)
    at gestionardom.Metodos.recorreDOMyMuestraPOrPantalla(Metodos.java:78)
    at gestionardom.GestionarDOM.main(GestionarDOM.java:36)
    C:\Users\Victor\AppData\Local\NetBeans\Cache\8.2\executor-snippets\run.xml:53: Java returned: 1
BUILD FAILED (total time: 0 seconds)

Mi pregunta es ¿ Porque al utilizar nodo.getAttributes().item(0).getNodeValue(); me da como valor null si debería devolverme el valor del nodo señalado?

metodos.java

package gestionardom;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

/**
 *
 * @author Victor
 */
public class Metodos {
    
    private Document doc; //Almacena del DOM del XML
    
    public int convertir_XML_DOM(File fichero){//COPIAR Y PEGAR ALWAYS
        // Creo un objeto DocumentBuilderFactory
        // Analiza un fichero XML para posteriormente generar el árbol DOM
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

        //Opciones: Sin espacios y sin comentarios
        factory.setIgnoringComments(true);
        factory.setIgnoringElementContentWhitespace(true);

        //Creo un objeto DocumentBuilder
        DocumentBuilder builder = null;


        try {
            //Cargo la estructura de árbol DOM con las opciones señaladas anteriormente
            builder = factory.newDocumentBuilder();
            try {
                //Interpreto el XML y genero el DOM correspondiente
                doc = builder.parse(fichero);
                return 0;
            } catch (SAXException e) {
                e.printStackTrace();
                return -1;
            } catch (IOException e) {
                e.printStackTrace();
               return -1;
            }

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

    }


    public String recorreDOMyMuestraPOrPantalla(){
        String salida = "";
        String[] datos_nodo = new String[3];

        Node nodo; //Nodo del árbol DOM
        Node raiz = doc.getFirstChild(); //Obtiene la raíz <libros>
        
        NodeList nodelist = raiz.getChildNodes(); //Obtiene los hijos del raíz <libro>

        //Recorremos los nodos, en este caso 4 <libro>
        for(int i=0; i<nodelist.getLength();i++){
            nodo = nodelist.item(i); //Almaceno el primer nodo, es decir, el primer <libro>
            

           datos_nodo = procesarLibro(nodo);


            if(nodo.getNodeType() == Node.ELEMENT_NODE){ //Comprobamos que sea un elemento
               salida = salida + "\n " +"Publicado en: " + datos_nodo[0]; //Nodo final
                salida = salida + "\n " +"El autor es: " + datos_nodo[1]; //Nodo final
                salida = salida + "\n " +"El título es: " + datos_nodo[2]; //Nodo final
                salida = salida + "\n" +"------------------------" + "\n";
            }
        }

        return salida;
    }



    public String[] procesarLibro(Node nodo){
        String datos[] = new String[3];
        //datos[0] --> Publicado en
        //datos[1] --> Titulo
        //datos[2] --> Autor

        Node temporal = null;
        int contador = 1;

        datos[0] = nodo.getAttributes().item(0).getNodeValue();

        NodeList nodos = nodo.getChildNodes();
        for(int i=0; i<nodos.getLength(); i++){
            temporal  = nodos.item(i);

            if(temporal.getNodeType() == Node.ELEMENT_NODE){
                datos[contador]  = temporal.getChildNodes().item(0).getNodeValue();
                contador++;
            }
        }

        return datos;
    }
    
}

GestionarDOM.java

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package gestionardom;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
public class GestionarDOM {

    /**
     * @param args the command line arguments
     */
    
    
    
    
    public static void main(String[] args) {
        
      File archivo = new File ("C:\\libros.xml");
      
      Metodos metodo= new Metodos();
      
      if(metodo.convertir_XML_DOM(archivo)==0)
      {
          
          metodo.recorreDOMyMuestraPOrPantalla();
          
      }
      else{
          System.out.print("No es posible leer el fichero");
      }

       
       
        
    }
    
    
    
    
}

libros.xml

<?xml version="1.0" encoding="UTF-8"?>
<Libros>
 <Libro publicado_en="1840">
   <Titulo>El Capote</Titulo>
   <Autor>Nikolai Gogol</Autor>
 </Libro>
 <Libro publicado_en="2008">
   <Titulo>El Sanador de Caballos</Titulo>
   <Autor>Gonzalo Giner</Autor>
 </Libro>
 <Libro publicado_en="1981">
   <Titulo>El Nombre de la Rosa</Titulo>
   <Autor>Umberto Eco</Autor>
 </Libro>
 <Libro publicado_en="1982">
   <Titulo>El libro de la selva</Titulo>
   <Autor>Carmen</Autor>
 </Libro>
</Libros>
Benito-B
  • 3,831
  • 3
  • 11
  • 28
victor96
  • 193
  • 2
  • 3
  • 14
  • 4
    Posible duplicado de [¿Cuál es la solución a todos los errores NullPointerException presentes, pasados y futuros?](https://es.stackoverflow.com/questions/42977/cu%c3%a1l-es-la-soluci%c3%b3n-a-todos-los-errores-nullpointerexception-presentes-pasados) – gbianchi Nov 23 '18 at 13:51
  • Se que un null se forma cuando el parámetro contiene un valor nulo, la pregunta es porque me da un valor nulo – victor96 Nov 23 '18 at 13:55
  • Fijate cual de todas las cosas en la linea que marca el error, es null. – gbianchi Nov 23 '18 at 13:59
  • nodo.getAttributes().item(0).getNodeValue(); me esta dando como valor nulo, y es lo que no entiendo, porque ese item existe y deberia devolverme el valor de su nodo – victor96 Nov 23 '18 at 14:02
  • Deberías especificar bien en tu pregunta cuál es la línea que lanza la excepción. – SJuan76 Nov 23 '18 at 14:41
  • 1
    Estas cometiendo un pequeño error. Si te sale la excepcion el problema no es el ultimo metodo "getNodeValue()", si no alguno de los anteriores. Es decir, nodo, getAttributes(), o item(0). – ZottoSL Nov 23 '18 at 14:45

3 Answers3

2

El problema es que algunos nodos no son tipo ELEMENT_NODE si no que son TEXT_NODE los cuales contienen el valor de salto de linea (\n) y esos tipos de nodos no contienen atributos. Creo que eso contesta a tu pregunta.

Ahora te dejare los inconvenientes que tienes en tu codigo, como solucionarlo o validarlos, y al final dejo unas pruebas que puedes realizar para veas del porque de tu excepcion.

Comenzemos:

Tu primer problema es que dentro del metodo recorreDOMyMuestraPOrPantalla especificamente dentro del for, estas mandando a llamar procesarLibro para luego validar si el nodo actual es un ELEMENT_NODE. No seria mejor validar primero el tipo de nodo para luego ejecutar procesarLibro.

Quedaria de esta forma, y con esto te funcionara:

if (nodo.getNodeType() == Node.ELEMENT_NODE) { //Comprobamos que sea un elemento
    datos_nodo = procesarLibro(nodo);
    salida = salida + "\n " + "Publicado en: " + datos_nodo[0]; //Nodo final
    salida = salida + "\n " + "El autor es: " + datos_nodo[1]; //Nodo final
    salida = salida + "\n " + "El título es: " + datos_nodo[2]; //Nodo final
    salida = salida + "\n" + "------------------------" + "\n";
}

Segundo problema, a pesar de que ahora se valida el tipo de nodo, tambien debes de validar dentro del metodo procesarLibro si el nodo que esta procesando contiene atributos.

// puedes validar con el metodo hasAttributes
if(!nodo.hasAttributes()) {
    return datos;
}

datos[0] = nodo.getAttributes().item(0).getNodeValue();

tambien se puede utilizar:

// verificando si el metodo getAttributes devuleve algun valor
if(nodo.getAttributes() == null) {
    return datos;
}

datos[0] = nodo.getAttributes().item(0).getNodeValue();

Te recomiendo utilizar hasAttributes(). Con estas dos modificaciones aseguras siempre procesar los nodos tipo ELEMENT_NODE y que los nodo tengan atributos.

Ahora la pregunta, Porque antes de esas validaciones arroja ese error.

Para contestar eso, volvamos al metodo recorreDOMyMuestraPOrPantalla y agregemos la siguientes lienas de codigo dentro del for, para imprimir la informacion basica del nodo:

for (int i = 0; i < nodelist.getLength(); i++) {
    nodo = nodelist.item(i);

    // imprimiendo informacion del nodo
    System.out.println(String.format("NodeName: %s\nNodeType: %s\nTextContent: %s",
            nodo.getNodeName(),
            nodo.getNodeType(),
            nodo.getTextContent()));

    if (nodo.getNodeType() == Node.ELEMENT_NODE) { //Comprobamos que sea un elemento
        datos_nodo = procesarLibro(nodo);
        salida = salida + "\n " + "Publicado en: " + datos_nodo[0]; //Nodo final
        salida = salida + "\n " + "El autor es: " + datos_nodo[1]; //Nodo final
        salida = salida + "\n " + "El título es: " + datos_nodo[2]; //Nodo final
        salida = salida + "\n" + "------------------------" + "\n";
    }
}

Con esto imprimimos el nombre, tipo y contenido del nodo actual. Con el xml que proporcionaste la salida seria esta:

NodeName: #text
NodeType: 3
TextContent: 

NodeName: Libro
NodeType: 1
TextContent: 
   El Capote
   Nikolai Gogol

NodeName: #text
NodeType: 3
TextContent: 

NodeName: Libro
NodeType: 1
TextContent: 
   El Sanador de Caballos
   Gonzalo Giner

NodeName: #text
NodeType: 3
TextContent: 

NodeName: Libro
NodeType: 1
TextContent: 
   El Nombre de la Rosa
   Umberto Eco

NodeName: #text
NodeType: 3
TextContent: 

NodeName: Libro
NodeType: 1
TextContent: 
   El libro de la selva
   Carmen

NodeName: #text
NodeType: 3
TextContent: 

Como se puede apreciar obtenemos 9 nodos y algunos nodos dicen #text y el tipo de nodo es 3, lo que equivale a TEXT_NODE y no al tipo ELEMENT_NODE que es el que quieres procesar.

Aqui dejo una pequeña porcion de codigo de la interfaz Node:

public interface Node {
    // NodeType
    public static final short ELEMENT_NODE = 1;
    public static final short TEXT_NODE    = 3;

    ...
}

Es por eso que te arroja una excepcion, ya que algunos nodos que procesas son de tipo texto, los cuales no contienen attributos.

¿Y porque tengo esos tipos de nodo si en el xml no los veos ni los e colocados?

Modifiquemos el xml, para responder eso.

Libros.xml modificado (todo en una linea):

<?xml version="1.0" encoding="UTF-8"?><Libros><Libro publicado_en="1840"><Titulo>El Capote</Titulo><Autor>Nikolai Gogol</Autor></Libro><Libro publicado_en="2008"><Titulo>El Sanador de Caballos</Titulo><Autor>Gonzalo Giner</Autor></Libro><Libro publicado_en="1981"><Titulo>El Nombre de la Rosa</Titulo><Autor>Umberto Eco</Autor></Libro><Libro publicado_en="1982"><Titulo>El libro de la selva</Titulo><Autor>Carmen</Autor></Libro></Libros>

Ejecutemos nuevamente y veremos que la salida es distinta:

NodeName: Libro
NodeType: 1
TextContent: El CapoteNikolai Gogol
NodeName: Libro
NodeType: 1
TextContent: El Sanador de CaballosGonzalo Giner
NodeName: Libro
NodeType: 1
TextContent: El Nombre de la RosaUmberto Eco
NodeName: Libro
NodeType: 1
TextContent: El libro de la selvaCarmen

Ya no contiene tipos TEXT_NODE = 3 y TextContent tiene la informacion pegada.

Entonces la razon es que el xml que estas leyendo tambien procesa los \n(saltos de lineas) como un nodo y esos nodos con valores de salto de linea (\n) no contienen atributos.

Al final tu metodo recorreDOMyMuestraPOrPantalla y procesarLibro basado en el xml que proporcionaste quedarian asi:

public String recorreDOMyMuestraPOrPantalla() {
    String salida = "";
    String[] datos_nodo = new String[3];

    Node nodo;
    Node raiz = doc.getFirstChild();

    NodeList nodelist = raiz.getChildNodes();

    for (int i = 0; i < nodelist.getLength(); i++) {
        nodo = nodelist.item(i);

        /* Test para obtener informacion de los nodos recorridos
        System.out.println(String.format("NodeName: %s\nNodeType: %s\nTextContent: %s",
                nodo.getNodeName(),
                nodo.getNodeType(),
                nodo.getTextContent()));
         */

        if (nodo.getNodeType() == Node.ELEMENT_NODE) {
            datos_nodo = procesarLibro(nodo);
            salida = salida + "\n " + "Publicado en: " + datos_nodo[0];
            salida = salida + "\n " + "El autor es: " + datos_nodo[1];
            salida = salida + "\n " + "El título es: " + datos_nodo[2];
            salida = salida + "\n" + "------------------------" + "\n";
        }
    }

    return salida;
}

public String[] procesarLibro(Node nodo) {
    String datos[] = new String[3];
    Node temporal = null;
    int contador = 1;

    if (!nodo.hasAttributes()) {
        return datos;
    }

    datos[0] = nodo.getAttributes().item(0).getNodeValue();

    NodeList nodos = nodo.getChildNodes();
    for (int i = 0; i < nodos.getLength(); i++) {
        temporal = nodos.item(i);

        if (temporal.getNodeType() == Node.ELEMENT_NODE) {
            datos[contador] = temporal.getChildNodes().item(0).getNodeValue();
            contador++;
        }
    }

    return datos;
}
Orlando De La Rosa
  • 3,015
  • 2
  • 12
  • 21
  • Pero así no me aparece, por ejemplo: Publicado en: tal fecha El autor es: tal, solo aparecen por pantalla los tipos de nodos – victor96 Nov 24 '18 at 18:47
  • Las pruebas que coloque solo era para que vieras del por que te saltaba la excepcion, basandome en tus comentarios donde decias que sabias que habia un valor nulo, pero no sabias porque. Si te fijas el test solo imprime el valor y tipo de los nodos y no el valor de los atributos. – Orlando De La Rosa Nov 24 '18 at 21:35
  • La informacion que nesecitas, ya la obtienes del metodo procesarLibro, no confundas el test que coloque con la informacion que nesecitas desplegar, reitero eso ya lo haces en procesarLibro. Solo comenta la linea donde se imprime la informacion de los nodos, por que esa linea solo es un tipo de prueba para saber que nodos se estan recorriendo. – Orlando De La Rosa Nov 24 '18 at 21:39
  • Pues me sigue apareciendo nulo haciendo tus pasos – victor96 Nov 26 '18 at 12:25
  • @victor96 al final de mi respuesta e colocado los dos metodos que nesecitas modificar, basado en el xml que proporcionaste. – Orlando De La Rosa Nov 26 '18 at 13:30
1

Hay varias posibilidades:

1- nodo.getAttributes() es nulo 2- item(0) es nulo 3- getNodeValue() es nulo

Debuga el código para averiguar esto y añade condiciones para evitar el null pointer exception, tal como:

if (node.getAttributes() != null) { ...
0

Yo haría tu método procesar libro así, y he detallado el código para que veas qué hace cada línea.

protected Libro procesarLibro(Node nodo) {

        Node nodoAux = null;
        Libro libro = new Libro();//instancio un obj libro
        //selecciono de la lista de los posibles atributos que tiene el nodo en este caso hay un solo atributo!!!
        libro.setLanzamiento(Integer.valueOf(nodo.getAttributes().item(0).getNodeValue())); //DUDA de siempre item 0

        //pillo los hijos de libro que en este caso son titulo + autor
        NodeList nodos = nodo.getChildNodes();

        for (int i = 0; i < nodos.getLength(); i++) {
            nodoAux = nodos.item(i);
            //es posible que haya nodos tipo Texto con \n al hacer cambio de linea del XML, así me asequro de que si no es tipo ELEMENT_NODE no se plasme en el FILE 
            if (nodoAux.getNodeType() == Node.ELEMENT_NODE) {
                // IMPORTANTE: para obtener el texto con el título accede al nodo TEXT del hijo y el nodoAux saca su valor
                if (nodoAux.getNodeName() == "Titulo") {
                    libro.setTitol(nodoAux.getChildNodes().item(0).getNodeValue());
                } else {//en este caso solo tengo titulo y autor y el else aki cuela
                    libro.setAutor(nodoAux.getChildNodes().item(0).getNodeValue());
                }
            }
        }
        
        return libro;
    }
ICodeForCaffeine
  • 1,282
  • 5
  • 17
Abel_yai
  • 1
  • 1