Saltar al contenido

Apuntes SAX en Java: SELECT (leer) y patrones para “INSERT/UPDATE/DELETE” reescribiendo

Con SAX no cargas el XML en memoria como un árbol (DOM), sino que lo procesas en streaming (evento a evento). Por eso:

  • SELECT (lectura/filtrado): natural con SAX.
  • INSERT/UPDATE/DELETE: no puedes “modificar” el documento cargado (no hay árbol). La forma habitual es leer y reescribir a un XML de salida aplicando los cambios en el momento adecuado (patrón streaming filter).

Ideas clave de SAX

  • Usa SAXParser + un DefaultHandler (o XMLReader + ContentHandler).
  • Sobrescribes:
    • startDocument() / endDocument()
    • startElement(...) / endElement(...)
    • characters(char[] ch, int start, int length)
  • Mantén estado con variables (p. ej. boolean enTitulo, String idActual, o una pila de etiquetas).
  • Es rápido y ligero (no consume mucha memoria), pero no permite cambios in situ.

Arranque básico (parser)

SAXParserFactory factory = SAXParserFactory.newInstance();
factory.setNamespaceAware(true); // si usas namespaces
SAXParser saxParser = factory.newSAXParser();
XMLReader reader = saxParser.getXMLReader();
reader.setContentHandler(new MiHandlerSelect()); // tu handler
reader.parse("Catalogo.xml");

SELECT (leer/recorrer con SAX)

Handler típico de lectura/filtrado

class MiHandlerSelect extends org.xml.sax.helpers.DefaultHandler {
    private StringBuilder buffer = new StringBuilder();
    private String etiquetaActual = "";
    private String idActual = null;
    private boolean dentroDeObjeto = false; // p.ej. <Libro>

    @Override
    public void startElement(String uri, String localName, String qName, org.xml.sax.Attributes atts) {
        etiquetaActual = qName;
        buffer.setLength(0); // limpiar

        if ("Libro".equals(qName)) {                // cambia por tu TAG_OBJETO
            dentroDeObjeto = true;
            idActual = atts.getValue("id");         // leer atributo si existe
        }
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        // Acumula porque SAX puede trocear el texto
        buffer.append(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String qName) {
        String texto = buffer.toString().trim();

        if (dentroDeObjeto) {
            if ("Titulo".equals(qName)) {
                // ejemplo de uso: imprimir/guardar el titulo
                // System.out.printf("Libro 
            }
            // ... otros campos
        }

        if ("Libro".equals(qName)) {
            dentroDeObjeto = false;
            idActual = null;
        }
        etiquetaActual = "";
    }
}

Búsqueda selectiva (por id o por condición)

  • Mantén idActual y en endElement solo procesa si cumple la condición: if ("Libro".equals(qName) && "123".equals(idActual)) { // mostrar datos acumulados / contadores / etc. }

Nota: con SAX no puedes hacer “getElementsByTagName”. El patrón es: reconocer que entras en el elemento objetivo en startElement, acumular datos, y decidir qué hacer al cerrar (endElement).


“INSERT / UPDATE / DELETE” con SAX (patrón reescritura)

Como no hay modificaciones directas, se usa un handler que además escribe un nuevo XML. Hay dos formas típicas:

  1. SAX + StAX: el handler escribe con XMLStreamWriter.
  2. SAXTransformerFactory (SAX → TransformerHandler) para encaminar eventos a un StreamResult.

Opción A: Handler + StAX (escritura manual)

import javax.xml.stream.*;

class FiltroEscrituraHandler extends org.xml.sax.helpers.DefaultHandler {
    private XMLStreamWriter out;
    private StringBuilder buffer = new StringBuilder();
    private boolean dentroDeLibro = false;
    private String idActual = null;

    FiltroEscrituraHandler(XMLStreamWriter out) {
        this.out = out;
    }

    @Override public void startDocument() {
        try { out.writeStartDocument("UTF-8", "1.0"); } catch (Exception ignored) {}
    }

    @Override public void endDocument() {
        try { out.writeEndDocument(); out.flush(); } catch (Exception ignored) {}
    }

    @Override
    public void startElement(String uri, String local, String qName, org.xml.sax.Attributes atts) {
        buffer.setLength(0);

        try {
            // --- DELETE: si este es el nodo a borrar, NO escribir startElement
            boolean esLibro = "Libro".equals(qName);
            if (esLibro) {
                dentroDeLibro = true;
                idActual = atts.getValue("id");
            }

            // Ejemplo DELETE de Libro con id=999: saltar escritura de todo el elemento
            if (esLibro && "999".equals(idActual)) {
                // Marcamos que vamos a omitir este bloque en endElement también
                // En este patrón, puedes usar un flag "omitirBloque" o un contador de profundidad
                omitir = true; profundidad = 1; 
                return;
            }

            if (omitir) { // si estamos omitiendo, solo contamos profundidad
                profundidad++;
                return;
            }

            // Escribir startElement y atributos tal cual
            out.writeStartElement(qName);
            for (int i = 0; i < atts.getLength(); i++) {
                out.writeAttribute(atts.getQName(i), atts.getValue(i));
            }

            // --- INSERT: si necesitas insertar un nuevo hijo justo al entrar en cierta etiqueta:
            // if ("Libros".equals(qName)) { escribirElementoInsertado(out); }
        } catch (Exception ignored) {}
    }

    // Flags auxiliares para manejar DELETE por bloque
    private boolean omitir = false;
    private int profundidad = 0;

    @Override
    public void endElement(String uri, String local, String qName) {
        try {
            if (omitir) {
                profundidad--;
                if (profundidad == 0) { omitir = false; } // cerramos el salto
                return;
            }

            String texto = buffer.toString();

            // --- UPDATE: si estamos en Libro id=123 y cerramos <Titulo>, reescribimos texto nuevo
            if ("Titulo".equals(qName) && "123".equals(idActual)) {
                texto = "Nuevo título actualizado";
            }

            // Escribir texto acumulado si lo hay
            if (!texto.isEmpty()) {
                out.writeCharacters(texto);
            }

            out.writeEndElement();

            if ("Libro".equals(qName)) {
                dentroDeLibro = false;
                idActual = null;
            }

            // --- INSERT: si queremos añadir un hermano después de cerrar cierto elemento, podemos escribirlo aquí.
            // if ("Libros".equals(qName)) { escribirElementoInsertado(out); }

        } catch (Exception ignored) {}
    }

    @Override
    public void characters(char[] ch, int start, int length) {
        buffer.append(ch, start, length);
    }

    // Ejemplo de método para insertar un nuevo <Libro>
    private void escribirElementoInsertado(XMLStreamWriter out) throws Exception {
        out.writeStartElement("Libro");
        out.writeAttribute("id", "1001");
        out.writeStartElement("Titulo"); out.writeCharacters("Libro insertado"); out.writeEndElement();
        out.writeStartElement("Autor");  out.writeCharacters("Autor X");        out.writeEndElement();
        out.writeEndElement(); // </Libro>
    }
}

Uso del filtro de escritura:

// Preparar escritor de salida
XMLOutputFactory xof = XMLOutputFactory.newInstance();
XMLStreamWriter xw = xof.createXMLStreamWriter(new java.io.FileWriter("Catalogo_out.xml"), "UTF-8");

// Parsers SAX
SAXParserFactory f = SAXParserFactory.newInstance();
SAXParser p = f.newSAXParser();
XMLReader r = p.getXMLReader();

// Handler que reescribe aplicando INSERT/UPDATE/DELETE
r.setContentHandler(new FiltroEscrituraHandler(xw));
r.parse("Catalogo.xml");
xw.close();

Notas clave de esta opción

  • DELETE: detecta el inicio del elemento a eliminar, activa omitir=true y lleva un contador de profundidad para saltar todo el subárbol.
  • UPDATE: modifica texto o atributos antes de escribirlos (normalmente al cerrar el elemento, porque ya acumulaste characters).
  • INSERT: escribe elementos adicionales en el punto deseado (al entrar o salir del padre adecuado).

Opción B: SAX → TransformerHandler (canalizar a salida)

Otra variante es usar SAXTransformerFactory para que tu handler reemita eventos hacia un TransformerHandler conectado a un StreamResult. Funciona parecido a A (insertas/filtras antes de reenviar), pero con menos manejo manual de etiquetas. Útil si quieres identación automática.

Esquema:

SAXTransformerFactory tf = (SAXTransformerFactory) SAXTransformerFactory.newInstance();
TransformerHandler th = tf.newTransformerHandler();
javax.xml.transform.Transformer t = th.getTransformer();
t.setOutputProperty(javax.xml.transform.OutputKeys.INDENT, "yes");
th.setResult(new javax.xml.transform.stream.StreamResult(new java.io.File("Catalogo_out.xml")));

// Tu handler intermedio puede extender XMLFilterImpl y enviar a 'th' los eventos startElement/characters/endElement

Patrones por operación (resumen mental)

  • SELECT
    Leer en streaming, acumular texto por etiqueta, filtrar con flags/condiciones, contar o mostrar.
  • INSERT
    Reescritura: cuando entres en <Coleccion> o cuando salgas, emites un nuevo <Objeto> completo (con atributos e hijos) hacia la salida.
  • UPDATE
    Reescritura: para atributos, cambia el valor en startElement antes de escribirlo; para texto, cambia el contenido en endElement usando el buffer acumulado.
  • DELETE
    Reescritura: al detectar el <Objeto> que quieres borrar, no emites sus eventos (start/characters/end) hasta cerrar el bloque (usa contador de profundidad).

Buenas prácticas con SAX

  • Acumula texto en characters (puede llegar troceado).
  • Usa flags o una pila (Deque<String>) para conocer el camino actual (ruta de etiquetas) si tu XML es complejo.
  • Para DELETE por bloque, contador de profundidad obligatorio.
  • Si necesitas reordenar elementos, SAX puro es incómodo; plantéate StAX o DOM puntualmente.
  • Valora validación (factory.setValidating(true)) si tienes DTD/XSD (y manejas ErrorHandler).

Diferencias rápidas SAX vs DOM (para el examen)

  • DOM: Carga todo el árbol, permite modificar en memoria y luego guardar. Más cómodo, más memoria.
  • SAX: Procesa en streaming, no permite modificar in situ; para cambiar algo, lees y reescribes. Más rápido y ligero para ficheros grandes.

Estos son apuntes (patrones y esqueletos). Adapta los nombres de etiquetas/atributos y la lógica de inserción/actualización/borrado a tu XML real. Si luego quieres, te preparo un mini esqueleto completo “lector → escritor” con un caso de ejemplo.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Información básica sobre protección de datos Ver más

  • Responsable: Tomas Gonzalez.
  • Finalidad:  Moderar los comentarios.
  • Legitimación:  Por consentimiento del interesado.
  • Destinatarios y encargados de tratamiento:  No se ceden o comunican datos a terceros para prestar este servicio.
  • Derechos: Acceder, rectificar y suprimir los datos.
  • Información Adicional: Puede consultar la información detallada en la Política de Privacidad.

¿Atascado con tu proyecto? Presupuesto GRATIS

X
error: Content is protected !!
Este sitio web utiliza cookies, si necesitas más información puedes visitar nuestra política de privacidad    Ver
Privacidad