JavaRush /Blog Java /Random-ES /Conceptos básicos de XML para el programador de Java - Pa...
Ярослав
Nivel 40
Днепр

Conceptos básicos de XML para el programador de Java - Parte 3.1 de 3 - SAX

Publicado en el grupo Random-ES
Introducción Hola a todos los lectores de mi aún no último artículo, y quiero felicitarlos: las cosas complicadas sobre XML quedaron atrás. Este artículo contendrá código en Java. Habrá un poco de teoría y luego práctica. Debido a que un material en SAX ocupaba 10 páginas en Word, me di cuenta de que no podía encajar en los límites. Por tanto, el artículo 3 se dividirá en 3 artículos separados, por extraño que parezca. Todo estará en este orden: SAX -> DOM -> JAXB. Este artículo se centrará únicamente en SAX. PD: Hubo una tarea en algún lugar del curso en la que era necesario mostrar todos los elementos internos en un archivo HTML. Después de este artículo, podrá hacer esto sin leer línea por línea con BufferedReaderalgoritmos de procesamiento complejos y convencionales, y también se dará una solución similar en el último ejemplo práctico. Comencemos :) SAX (API simple para XML): TEORÍA El controlador SAX está diseñado de tal manera que simplemente lee archivos XML secuencialmente y reacciona a diferentes eventos, después de lo cual pasa la información a un controlador de eventos especial. Tiene bastantes eventos, pero los más frecuentes y útiles son los siguientes:
  1. startDocument— el comienzo del documento
  2. endDocument- fin del documento
  3. startElement- abrir un elemento
  4. endElement- cerrar un elemento
  5. characters— información de texto dentro de los elementos.
Todos los eventos se procesan en un controlador de eventos , que debe crearse y anularse los métodos . Ventajas: alto rendimiento debido al método "directo" de lectura de datos, bajos costos de memoria. Desventajas: funcionalidad limitada, lo que significa que en problemas no lineales tendremos que perfeccionarla. SAX (API simple para XML) – PRACTICA Inmediatamente una lista de importaciones para que no busques ni confundas nada:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
Ahora, primero, necesitamos crear un SAXParser:
public class SAXExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Creación фабрики и образца analizadorа
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }
}
Como puede ver, primero debe crear una fábrica y luego crear el analizador en la fábrica. Ahora que tenemos el analizador, necesitamos un controlador para sus eventos. Para esto necesitamos una clase separada para nuestra propia conveniencia:
public class SAXExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }

    private static class XMLHandler extends DefaultHandler {
        @Override
        public void startDocument() throws SAXException {
            // Тут будет логика реакции на начало documentoа
        }

        @Override
        public void endDocument() throws SAXException {
            // Тут будет логика реакции на конец documentoа
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            // Тут будет логика реакции на начало elemento
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            // Тут будет логика реакции на конец elemento
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            // Тут будет логика реакции на текст между elementoми
        }

        @Override
        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
            // Тут будет логика реакции на пустое пространство внутри элементов (пробелы, переносы строчек и так далее).
        }
    }
}
Creamos una clase con todos los métodos que necesitábamos para manejar los eventos enumerados en la teoría. Un poco más de teoría adicional: Un poco sobre characters: si el elemento contiene texto, por ejemplo, " hola ", entonces, teóricamente, el método se puede llamar 5 veces seguidas para cada carácter individual, pero esto no da miedo, ya que todo seguirá funcionando. Acerca de los métodos startElementy endElement:uri - este es el espacio en el que se encuentra el elemento, localName- este es el nombre del elemento sin prefijo, qName- este es el nombre del elemento con prefijo (si lo hay, en caso contrario solo el nombre del elemento). uriy localNamesiempre vacío si no hemos habilitado el procesamiento de espacio en fábrica. Esto se hace usando el método de fábrica setNamespaceAware(true). Luego podemos obtener espacios ( uri) y elementos con prefijos delante ( localName). Tarea n.º 1: tenemos el siguiente XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
    <name>IT-Heaven</name>
    <offices>
        <office floor="1" room="1">
            <employees>
                <employee name="Maksim" job="Middle Software Developer" />
                <employee name="Ivan" job="Junior Software Developer" />
                <employee name="Franklin" job="Junior Software Developer" />
            </employees>
        </office>
        <office floor="1" room="2">
            <employees>
                <employee name="Herald" job="Middle Software Developer" />
                <employee name="Adam" job="Middle Software Developer" />
                <employee name="Leroy" job="Junior Software Developer" />
            </employees>
        </office>
    </offices>
</company>
Nuestro objetivo: obtener toda la información de todos los empleados de este archivo. Primero, necesitamos crear una claseEmployee:
public class Employee {
    private String name, job;

    public Employee(String name, String job) {
        this.name = name;
        this.job = job;
    }

    public String getName() {
        return name;
    }

    public String getJob() {
        return job;
    }
}
Y en nuestra clase principal SAXExamplenecesitamos una lista con todos los empleados:
private static ArrayList<Employee> employees = new ArrayList<>();
Ahora veamos detenidamente dónde está la información que necesitamos en el archivo XML. Y, como podemos ver, toda la información que necesitamos son los atributos de los elementos employee. Y como startElementtenemos un parámetro tan útil como attributes, entonces tenemos una tarea bastante sencilla. Primero, eliminemos los métodos innecesarios para no saturar nuestro código. Sólo necesitamos el startElement. Y en el método en sí, debemos recopilar información de los atributos de la etiqueta de empleado. Atención:
public class SAXExample {
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }

    private static class XMLHandler extends DefaultHandler {
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (qName.equals("employee")) {
                String name = attributes.getValue("name");
                String job = attributes.getValue("job");
                employees.add(new Employee(name, job));
            }
        }
    }
}
La lógica es sencilla: si el nombre de un elemento es employee, simplemente recibiremos información sobre sus atributos. Existe attributesun método útil mediante el cual, conociendo el nombre de un atributo, se puede obtener su valor. Eso es lo que usamos. Ahora que hemos creado un controlador de eventos para el comienzo de un elemento, necesitamos analizar nuestro archivo XML . Para hacer esto, simplemente haz esto:
public class SAXExample {
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        XMLHandler handler = new XMLHandler();
        parser.parse(new File("resource/xml_file1.xml"), handler);

        for (Employee employee : employees)
            System.out.println(String.format("Nombre сотрудника: %s, его должность: %s", employee.getName(), employee.getJob()));
    }

    private static class XMLHandler extends DefaultHandler {
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (qName.equals("employee")) {
                String name = attributes.getValue("name");
                String job = attributes.getValue("job");
                employees.add(new Employee(name, job));
            }
        }
    }
}
En el método de análisis debes pasar la ruta al archivo xml y el controlador que creaste. Y así, usando este código extrajimos información de este XML:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<company>
    <name>IT-Heaven</name>
    <offices>
        <office floor="1" room="1">
            <employees>
                <employee name="Maksim" job="Middle Software Developer" />
                <employee name="Ivan" job="Junior Software Developer" />
                <employee name="Franklin" job="Junior Software Developer" />
            </employees>
        </office>
        <office floor="1" room="2">
            <employees>
                <employee name="Herald" job="Middle Software Developer" />
                <employee name="Adam" job="Middle Software Developer" />
                <employee name="Leroy" job="Junior Software Developer" />
            </employees>
        </office>
    </offices>
</company>
Y obtuvimos el siguiente resultado:
Nombre сотрудника: Maksim, его должность: Middle Software Developer
Nombre сотрудника: Ivan, его должность: Junior Software Developer
Nombre сотрудника: Franklin, его должность: Junior Software Developer
Nombre сотрудника: Herald, его должность: Middle Software Developer
Nombre сотрудника: Adam, его должность: Middle Software Developer
Nombre сотрудника: Leroy, его должность: Junior Software Developer
¡Misión cumplida! Tarea n.º 2: tenemos el siguiente XML:
<?xml version="1.0" encoding="UTF-8"?>
<company>
    <name>IT-Heaven</name>
    <offices>
        <office floor="1" room="1">
            <employees>
                <employee>
                    <name>Maksim</name>
                    <job>Middle Software Developer</job>
                </employee>
                <employee>
                    <name>Ivan</name>
                    <job>Junior Software Developer</job>
                </employee>
                <employee>
                    <name>Franklin</name>
                    <job>Junior Software Developer</job>
                </employee>
            </employees>
        </office>
        <office floor="1" room="2">
            <employees>
                <employee>
                    <name>Herald</name>
                    <job>Middle Software Developer</job>
                </employee>
                <employee>
                    <name>Adam</name>
                    <job>Middle Software Developer</job>
                </employee>
                <employee>
                    <name>Leroy</name>
                    <job>Junior Software Developer</job>
                </employee>
            </employees>
        </office>
    </offices>
</company>
Nuestro objetivo: obtener toda la información de todos los empleados de este archivo. Este problema demostrará bien cómo un archivo XML mal estructurado puede dificultar la escritura de código. Como puede ver, la información sobre el nombre y la posición ahora se almacena como información de texto dentro de los elementos namey job. Para leer texto dentro de elementos, tenemos el método de caracteres. Para hacer esto, necesitamos crear una nueva clase de controlador con lógica mejorada. No olvide que los controladores son clases completas capaces de almacenar lógica de cualquier complejidad. Por tanto, ahora sintonizaremos nuestro procesador. De hecho, basta con señalar que siempre nos nameturnamos job, y no importa en qué orden, podemos guardar fácilmente el nombre y la profesión en variables separadas, y cuando ambas variables estén guardadas, crear nuestro empleado. Solo que aquí, junto con el comienzo del elemento, no tenemos un parámetro para el texto dentro del elemento. Necesitamos usar métodos en texto. Pero, ¿cómo obtenemos información de texto dentro de un elemento si son métodos completamente diferentes? Mi solución: solo necesitamos recordar el nombre del último elemento y characterscomprobar en qué elemento estamos leyendo la información. También debe recordar que los caracteres <codee> leen todos los caracteres dentro de los elementos, lo que significa que se leerán todos los espacios e incluso los saltos de línea. Y no los necesitamos. Necesitamos ignorar estos datos porque son incorrectos.</codee> Código:
public class SAXExample {
    private static ArrayList<Employee> employees = new ArrayList<>();

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        AdvancedXMLHandler handler = new AdvancedXMLHandler();
        parser.parse(new File("resource/xml_file2.xml"), handler);

        for (Employee employee : employees)
            System.out.println(String.format("Nombre сотрудника: %s, его должность: %s", employee.getName(), employee.getJob()));
    }

    private static class AdvancedXMLHandler extends DefaultHandler {
        private String name, job, lastElementName;

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            lastElementName = qName;
        }

        @Override
        public void characters(char[] ch, int start, int length) throws SAXException {
            String information = new String(ch, start, length);

            information = information.replace("\n", "").trim();

            if (!information.isEmpty()) {
                if (lastElementName.equals("name"))
                    name = information;
                if (lastElementName.equals("job"))
                    job = information;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if ( (name != null && !name.isEmpty()) && (job != null && !job.isEmpty()) ) {
                employees.add(new Employee(name, job));
                name = null;
                job = null;
            }
        }
    }
}
Como puede ver, debido a la banal complicación de la estructura del archivo XML, nuestro código se ha vuelto mucho más complicado. Sin embargo, el código no es complicado. Descripción: creamos variables para almacenar datos sobre el empleado ( name, job) , así como una variable lastElementNamepara registrar en qué elemento estamos. Después de esto, en el método charactersfiltramos la información, y si todavía hay información allí, entonces esto significa que este es el texto que necesitamos, y luego determinamos si es un nombre o una profesión usando lastElementName. En el método endElementcomprobamos si se ha leído toda la información y, de ser así, creamos un empleado y reseteamos la información. El resultado de la solución es equivalente al primer ejemplo:
Nombre сотрудника: Maksim, его должность: Middle Software Developer
Nombre сотрудника: Ivan, его должность: Junior Software Developer
Nombre сотрудника: Franklin, его должность: Junior Software Developer
Nombre сотрудника: Herald, его должность: Middle Software Developer
Nombre сотрудника: Adam, его должность: Middle Software Developer
Nombre сотрудника: Leroy, его должность: Junior Software Developer
Así, este problema ha quedado solucionado , pero se puede notar que la complejidad es mayor. Por lo tanto, podemos concluir que almacenar información de texto en atributos será más correcto en la mayoría de los casos que en elementos individuales. Y una tarea más dulce que resolverá parcialmente el problema en JavaRush sobre mostrar información sobre un elemento en HTML, solo que será necesario editarlo un poco, aquí simplemente enumeraremos todos los elementos dentro de un elemento :) Tarea No. 3 - dado el elemento elemento, muestra los nombres y atributos de todos los elementos internos; si no se encuentra el elemento, muéstralo. Para esta tarea utilizaremos el siguiente archivo XML:
<?xml version="1.0" encoding="UTF-8"?>
<root>
    <oracle>
        <connection value="jdbc:oracle:thin:@10.220.140.48:1521:test1" />
        <user value="secretOracleUsername" />
        <password value="111" />
    </oracle>

    <mysql>
        <connection value="jdbc:mysql:thin:@10.220.140.48:1521:test1" />
        <user value="secretMySQLUsername" />
        <password value="222" />
    </mysql>
</root>
Como puede ver, tenemos tres escenarios posibles aquí: root, mysql, oracle. Luego el programa mostrará toda la información sobre todos los elementos del interior. ¿Cómo podemos hacer esto? Y es bastante simple: solo necesitamos declarar una variable lógica isEntered, que indicará si necesitamos el elemento dentro, y si está dentro, leer todos los datos startElement. Código de solución:
public class SAXExample {
    private static boolean isFound;

    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();

        SearchingXMLHandler handler = new SearchingXMLHandler("root");
        parser.parse(new File("resource/xml_file3.xml"), handler);

        if (!isFound)
            System.out.println("Элемент не был найден.");
    }

    private static class SearchingXMLHandler extends DefaultHandler {
        private String element;
        private boolean isEntered;

        public SearchingXMLHandler(String element) {
            this.element = element;
        }

        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
            if (isEntered) {
                System.out.println(String.format("Найден элемент <%s>, его атрибуты:", qName));

                int length = attributes.getLength();
                for(int i = 0; i < length; i++)
                    System.out.println(String.format("Nombre атрибута: %s, его significado: %s", attributes.getQName(i), attributes.getValue(i)));
            }

            if (qName.equals(element)) {
                isEntered = true;
                isFound = true;
            }
        }

        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException {
            if (qName.equals(element))
                isEntered = false;
        }
    }
}
En este código, al ingresar un elemento sobre el cual necesitamos información, configuramos la bandera isEntereden verdadero, lo que significa que estamos dentro del elemento. Y tan pronto como estemos dentro del elemento, simplemente procesamos cada nuevo elemento startElement, sabiendo que es exactamente un elemento interno de nuestro elemento. Entonces generamos el nombre del elemento y su título. Si el elemento no se encontró en el archivo, entonces tenemos una variable isFoundque se establece cuando se encuentra el elemento, y si es falsa, se mostrará un mensaje de que no se encontró el elemento. Y como puedes ver, en el ejemplo SearchingXMLHandlerpasamos rootun elemento al constructor. Conclusión para él:
Найден элемент <oracle>, его атрибуты:
Найден элемент <connection>, его атрибуты:
Nombre атрибута: value, его significado: jdbc:oracle:thin:@10.220.140.48:1521:test1
Найден элемент <user>, его атрибуты:
Nombre атрибута: value, его significado: secretOracleUsername
Найден элемент <password>, его атрибуты:
Nombre атрибута: value, его significado: 111
Найден элемент <mysql>, его атрибуты:
Найден элемент <connection>, его атрибуты:
Nombre атрибута: value, его significado: jdbc:mysql:thin:@10.220.140.48:1521:test1
Найден элемент <user>, его атрибуты:
Nombre атрибута: value, его significado: secretMySQLUsername
Найден элемент <password>, его атрибуты:
Nombre атрибута: value, его significado: 222
Así, recibimos toda la información sobre los elementos internos y sus atributos. El problema esta resuelto. <h2>Epílogo</h2>Has visto que SAX es una herramienta bastante interesante y bastante efectiva, y se puede utilizar de diferentes maneras, para diferentes propósitos, etc., solo necesitas mirar el problema desde la derecha. lado, como se muestra en las tareas No. 2 y No. 3, donde SAX no proporcionó métodos directos para resolver el problema, pero, gracias a nuestro ingenio, pudimos encontrar una salida a la situación. La siguiente parte del artículo estará enteramente dedicada al DOM. Espero que hayas disfrutado conociendo SAX. Experimenta, practica y entenderás que todo es bastante sencillo. Y eso es todo, buena suerte con tu programación y esperamos pronto la parte sobre el DOM. Buena suerte en tus estudios :) Artículo anterior: [Concurso] Conceptos básicos de XML para un programador de Java - Parte 2 de 3 Artículo siguiente: [Concurso] Conceptos básicos de XML para un programador de Java - Parte 3.2 de 3 - DOM
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION