- Inmutabilidad
- Acceso a subnodos por invocaciones encadenadas, evitando NPE ante nodos inexistentes
El segundo punto se basa en el patrón Null Object: la idea es prescindir de null en el API para evitar el fatídico Null Pointer Exception. Más abajo lo vemos.
Como dije antes la librería es elemental, y sus limitaciones son:
- No sirve para "recorrer" el XML sino para obtener elementos conocidos
- No accede a los atributos de los elementos
API de XmlNode
Esta es la firma de los constructores:
public XmlNode(); public XmlNode(Node node); public XmlNode(Document doc);El primero es el Null Object, y si bien es un constructor público, lo normal será que sólo se utilice desde la misma clase y no desde afuera. El segundo toma un Node y el tercero un Document. Este último toma el nodo raiz del documento. La idea es que podamos partir de un documento o un nodo cualquiera, y olvidarnos de la diferenciación: lo que tenemos es un árbol de nodos.
public String getName(); public String getValue();
Con estos métodos obtenemos el nombre (tag) y el valor (contenido) del nodo. Ambos retornarán null si es un Null Object.
public XmlNode subNode(String tagName);
Este método en cambio nunca retorna null y permite invocaciones encadenadas. El argumento debe ser un tag de un nodo hijo (inmediatamente relacionado con el actual). Veamos un ejemplo. Si creamos un XmlNode a partir de esto:
<a> <b> <c>hola</c> <d>que tal</d> </b> </a>
Podremos accederlo así:
String nombre = nodo.getName(); // "a" XmlNode nodoB = nodo.subNode("b"); // subnodo <b> XmlNode nodoC = nodo.subNode("b").subNode("c"); // subnodo <c> String valor1 = nodo.subNode("b").subNode("d").getValue(); // "que tal" String valor2 = nodoC.getValue(); // "hola"
Y también así:
XmlNode nodoX = nodo.subNode("z").subNode("y").subNode("x"); String valor3 = nodoX.getValue(); // null
Ahí vemos la invocación encadenada con nodos inexistentes. En nodoX nos quedó un Null Object. Para verificar si el XmlNode "está definido" o es nulo, tenemos también el siguiente método (inspirado en el homónimo de Option en Scala):
public boolean isDefined();
Así será más prolijo verificar si el nodo existe, en lugar de ver si getName() o getValue() retornan null.
El código
import org.w3c.dom.Document; import org.w3c.dom.Node; import org.w3c.dom.NodeList; public class XmlNode { protected Node node = null; public XmlNode() { } public XmlNode(Node node) { this.node = node; } public XmlNode(Document doc) { this.node = doc.getDocumentElement(); } public XmlNode subNode(String tagName) { if(node != null) { NodeList nl = node.getChildNodes(); for(int i = 0; i < nl.getLength(); i++) { Node n = nl.item(i); if(n.getNodeName().equals(tagName)) { return new XmlNode(n); } } } return new XmlNode(); } public String getName() { return !this.isDefined()? null : node.getNodeName(); } public String getValue() { return !this.isDefined()? null : node.getFirstChild().getNodeValue(); } public boolean isDefined() { return this.node != null; } }
Muchas gracias pero no me esta funcionando con un xml con espacio de nombres ejemplo ... etc
ResponderEliminarespero puedas echarme una mano gracias
¿El xml pudiste cargarlo exitosamente en un org.w3c.dom.Document? ¿qué tipo de problema tuviste después con XmlNode?
EliminarOtra limitación obvia que me olvidé de mencionar es que no se retornan colecciones de nodos. Es un wrapper muy elemental y para muchos fines prácticos se queda corto.