A partir de tu helper:
private static Document cargarDocumento(String ruta) throws Exception {
File archivo = new File(ruta);
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(archivo);
doc.getDocumentElement().normalize();
return doc;
}
Ideas clave del DOM
- Document es todo el árbol XML en memoria (mutable).
- Element representa etiquetas; Attr atributos; Text el contenido.
- Muchas operaciones empiezan con
doc.getElementsByTagName("...")
o navegando congetChildNodes()
. - Tras modificar el árbol, hay que guardarlo con
Transformer
.
SELECT (leer/recorrer)
Patrón básico por etiqueta
NodeList nodos = doc.getElementsByTagName("TAG_OBJETO"); // p.ej. "Libro"
for (int i = 0; i < nodos.getLength(); i++) {
Node nodo = nodos.item(i);
if (nodo.getNodeType() == Node.ELEMENT_NODE) {
Element e = (Element) nodo;
// Leer atributo
String id = e.getAttribute("id"); // si existe
// Leer subelemento (siempre comprobar nulls/length)
Element tituloEl = (Element) e.getElementsByTagName("Titulo").item(0);
String titulo = (tituloEl != null) ? tituloEl.getTextContent() : "";
// ...procesar/mostrar
}
}
Búsqueda por atributo “id”
Element buscarPorId(Document doc, String tag, String idBuscado) {
NodeList nl = doc.getElementsByTagName(tag);
for (int i = 0; i < nl.getLength(); i++) {
Element e = (Element) nl.item(i);
if (idBuscado.equals(e.getAttribute("id"))) return e;
}
return null;
}
Nota: si necesitas consultas más potentes, puedes usar XPath (opcional en apuntes), pero con DOM puro suele bastar.
INSERT (crear y añadir nodos)
Patrón básico (crear elemento con hijos y atributos)
void insertarObjeto(Document doc) {
// 1) Crear el nuevo elemento
Element nuevo = doc.createElement("TAG_OBJETO"); // p.ej. "Libro"
nuevo.setAttribute("id", "123"); // atributo opcional
// 2) Crear y añadir subelementos
Element titulo = doc.createElement("Titulo");
titulo.setTextContent("Título de ejemplo");
nuevo.appendChild(titulo);
Element autor = doc.createElement("Autor");
autor.setTextContent("Nombre Apellidos");
nuevo.appendChild(autor);
// 3) Insertar en el padre adecuado (p.ej. raíz o una colección)
Element raiz = doc.getDocumentElement(); // p.ej. "Catalogo"
raiz.appendChild(nuevo);
// (Opcional) Insertar en posición concreta:
// raiz.insertBefore(nuevo, raiz.getFirstChild());
}
Buenas prácticas al insertar
- Decide el padre correcto (raíz o un contenedor tipo
<Libros>
). - Define siempre los atributos obligatorios (p.ej.
id
) antes de guardar. - Si el orden importa, usa
insertBefore
.
UPDATE (modificar contenido/atributos)
Cambiar texto de un subelemento
void actualizarTitulo(Document doc, String idLibro, String nuevoTitulo) {
Element libro = buscarPorId(doc, "Libro", idLibro);
if (libro == null) return;
Element titulo = (Element) libro.getElementsByTagName("Titulo").item(0);
if (titulo == null) {
titulo = doc.createElement("Titulo");
libro.appendChild(titulo);
}
titulo.setTextContent(nuevoTitulo);
}
Cambiar/añadir un atributo
void actualizarAtributo(Document doc, String idLibro, String nombreAttr, String valor) {
Element libro = buscarPorId(doc, "Libro", idLibro);
if (libro != null) {
libro.setAttribute(nombreAttr, valor); // crea si no existe
}
}
Reemplazar un nodo completo
void reemplazarNodo(Document doc, Element viejo, Element nuevo) {
Node padre = viejo.getParentNode();
padre.replaceChild(nuevo, viejo);
}
Consejos UPDATE
- Comprueba
null
/longitudes antes deitem(0)
. - Si el subelemento no existe, créalo para mantener el esquema consistente.
DELETE (eliminar nodos)
Borrar un elemento por id
void borrarPorId(Document doc, String idLibro) {
Element libro = buscarPorId(doc, "Libro", idLibro);
if (libro != null) {
Node padre = libro.getParentNode();
padre.removeChild(libro);
}
}
Borrado seguro en bucles
Si vas a borrar varios elementos mientras iteras un NodeList
, itera al revés:
NodeList nl = doc.getElementsByTagName("Libro");
for (int i = nl.getLength() - 1; i >= 0; i--) {
Element e = (Element) nl.item(i);
if (/* condición de borrado */) {
e.getParentNode().removeChild(e);
}
}
Guardar cambios a disco (obligatorio tras INSERT/UPDATE/DELETE)
static void guardarDocumento(Document doc, String ruta) throws Exception {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer t = tf.newTransformer();
t.setOutputProperty(OutputKeys.INDENT, "yes");
t.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
// (Opcional) Mejorar sangría con implementaciones que soporten:
// t.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(ruta));
t.transform(source, result);
}
Notas de guardado
- Si no llamas a
guardarDocumento
, los cambios quedan solo en memoria. - Asegura codificación (
UTF-8
) si manejas tildes/ñ.
Utilidades y buenas prácticas
- Factory configurado: cuando trabajes con namespaces o quieras ignorar espacios, puedes ajustar:
DocumentBuilderFactory f = DocumentBuilderFactory.newInstance(); f.setNamespaceAware(true); // f.setIgnoringElementContentWhitespace(true); // requiere validación DTD
- normalize() (ya lo haces) ayuda a juntar nodos de texto contiguos.
- Comprobación de tipos:
if (n.getNodeType() == Node.ELEMENT_NODE)
antes de castear. - Validación: si tu XML tiene DTD/XSD, valida para detectar errores de esquema.
- IDs únicos: si usas
id
como clave, mantén un generador/validador de unicidad. - Errores comunes:
NullPointerException
por asumir queitem(0)
existe.- Guardar sin
INDENT
y luego “perder” legibilidad (no es error funcional, pero molesta). - Borrar en orden ascendente (puede saltarse nodos).
Esqueleto típico de menú (solo guía, no solución)
Document doc = cargarDocumento("Catalogo.xml");
switch(opcion) {
case 1: // SELECT
// listar/filtrar y mostrar campos
break;
case 2: // INSERT
// construir elemento y append al padre
// guardarDocumento(doc, "Catalogo.xml");
break;
case 3: // UPDATE
// localizar por id/criterio, setTextContent/setAttribute
// guardarDocumento(doc, "Catalogo.xml");
break;
case 4: // DELETE
// localizar y removeChild
// guardarDocumento(doc, "Catalogo.xml");
break;
}
Con esto tienes los patrones de trabajo para SELECT/INSERT/UPDATE/DELETE usando DOM, sin resolver un ejercicio concreto. Solo adapta los
TAG_...
, nombres de campos y criterios a tu XML real.