JavaRush /Blog Java /Random-FR /Notions de base XML pour le programmeur Java - Partie 3.1...
Ярослав
Niveau 40
Днепр

Notions de base XML pour le programmeur Java - Partie 3.1 sur 3 - SAX

Publié dans le groupe Random-FR
Introduction Bonjour à tous les lecteurs de mon pas encore dernier article, et je tiens à vous féliciter : les choses compliquées concernant XML sont derrière nous. Cet article contiendra du code en Java. Il y aura un peu de théorie, puis de pratique. Étant donné qu’un document sur SAX remplissait 10 pages dans Word, j’ai réalisé que je ne pouvais pas respecter les limites. Par conséquent, l’article 3 sera divisé en 3 articles distincts, aussi étrange que cela puisse paraître. Tout sera dans cet ordre : SAX -> DOM -> JAXB. Cet article se concentrera uniquement sur SAX. PS Il y avait une tâche quelque part dans le cours où il fallait afficher tous les éléments internes dans un fichier HTML. Après cet article, vous pourrez le faire sans lire ligne par ligne avec BufferedReaderdes algorithmes de traitement conventionnels et complexes, et une solution similaire sera également donnée dans le dernier exemple pratique. Commençons :) SAX (API simple pour XML) - THÉORIE Le gestionnaire SAX est conçu de telle manière qu'il lit simplement les fichiers XML de manière séquentielle et réagit à différents événements, après quoi il transmet les informations à un gestionnaire d'événements spécial. Il comporte de nombreux événements, mais les plus fréquents et les plus utiles sont les suivants :
  1. startDocument— le début du document
  2. endDocument- fin du document
  3. startElement- ouvrir un élément
  4. endElement- fermer un élément
  5. characters— informations textuelles à l'intérieur des éléments.
Tous les événements sont traités dans un gestionnaire d'événements , qui doit être créé et les méthodes remplacées . Avantages : hautes performances grâce à la méthode « directe » de lecture des données, faibles coûts de mémoire. Inconvénients : fonctionnalité limitée, ce qui signifie que dans les problèmes non linéaires nous devrons l'affiner. SAX (Simple API for XML) – PRATIQUE Immédiatement une liste des importations pour ne rien chercher et ne rien confondre :
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;
Maintenant, nous devons d’abord créer un SAXParser :
public class SAXExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Creation фабрики и образца parserа
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }
}
Comme vous pouvez le constater, vous devez d'abord créer une usine, puis créer l'analyseur lui-même dans l'usine. Maintenant que nous avons l'analyseur lui-même, nous avons besoin d'un gestionnaire pour ses événements. Pour cela, nous avons besoin d’une classe distincte pour notre propre commodité :
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 {
            // Тут будет логика реакции на начало documentа
        }

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

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

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

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

        @Override
        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException {
            // Тут будет логика реакции на пустое пространство внутри элементов (пробелы, переносы строчек и так далее).
        }
    }
}
Nous avons créé une classe avec toutes les méthodes dont nous avions besoin pour gérer les événements répertoriés dans la théorie. Un peu plus de théorie supplémentaire : Un peu sur characters: si l'élément contient du texte, par exemple « bonjour », alors, théoriquement, la méthode peut être appelée 5 fois de suite pour chaque caractère individuel, mais ce n'est pas effrayant, puisque tout fonctionnera toujours. A propos des méthodes startElementet endElement:uri - c'est l'espace dans lequel se trouve l'élément, localName- c'est le nom de l'élément sans préfixe, qName- c'est le nom de l'élément avec un préfixe (s'il y en a un, sinon juste le nom de l'élément). uriet localNametoujours vide si nous n'avons pas activé le traitement de l'espace en usine. Cela se fait en utilisant la méthode d'usine setNamespaceAware(true). Ensuite, nous pouvons obtenir un espace ( uri) et des éléments avec des préfixes devant eux ( localName). Tâche n°1 – Nous avons le XML suivant
<?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>
Notre objectif : récupérer toutes les informations sur tous les salariés de ce fichier. Tout d'abord, nous devons créer une classeEmployee:
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;
    }
}
Et dans notre classe principale, SAXExamplenous avons besoin d'une liste de tous les employés :
private static ArrayList<Employee> employees = new ArrayList<>();
Examinons maintenant attentivement où se trouvent les informations dont nous avons besoin dans le fichier XML. Et, comme nous pouvons le voir, toutes les informations dont nous avons besoin sont les attributs des éléments employee. Et puisque startElementnous avons un paramètre aussi utile que attributes, nous avons alors une tâche assez simple. Tout d’abord, supprimons les méthodes inutiles afin de ne pas encombrer notre code. Nous avons seulement besoin du startElement. Et dans la méthode elle-même, nous devons collecter des informations à partir des attributs du tag employé. Attention:
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 logique est simple : si le nom d'un élément est employee, nous recevrons simplement des informations sur ses attributs. Il attributesexiste une méthode utile grâce à laquelle, connaissant le nom d'un attribut, vous pouvez obtenir sa valeur. C'est ce que nous avons utilisé. Maintenant que nous avons créé un gestionnaire d'événements pour le début d'un élément, nous devons analyser notre fichier XML . Pour ce faire, faites simplement ceci :
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("Name сотрудника: %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));
            }
        }
    }
}
Dans la méthode d'analyse, vous devez transmettre le chemin d'accès au fichier XML et au gestionnaire que vous avez créé. Et donc, en utilisant ce code, nous avons extrait les informations de ce 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>
Et nous avons obtenu le résultat suivant :
Name сотрудника: Maksim, его должность: Middle Software Developer
Name сотрудника: Ivan, его должность: Junior Software Developer
Name сотрудника: Franklin, его должность: Junior Software Developer
Name сотрудника: Herald, его должность: Middle Software Developer
Name сотрудника: Adam, его должность: Middle Software Developer
Name сотрудника: Leroy, его должность: Junior Software Developer
Mission accomplie! Tâche n°2 - nous avons le XML suivant :
<?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>
Notre objectif : récupérer toutes les informations sur tous les salariés de ce fichier. Ce problème démontrera bien comment un fichier XML mal structuré peut rendre l'écriture de code plus difficile. Comme vous pouvez le constater, les informations sur le nom et la position sont désormais stockées sous forme d'informations textuelles à l'intérieur des éléments nameet job. Pour lire du texte à l’intérieur des éléments, nous avons la méthode des caractères. Pour ce faire, nous devons créer une nouvelle classe de gestionnaire avec une logique améliorée. N'oubliez pas que les gestionnaires sont des classes à part entière capables de stocker une logique de toute complexité. Par conséquent, nous allons maintenant régler notre processeur. En fait, il suffit de noter que nous nous relayons toujours name, jobet peu importe dans quel ordre, nous pouvons facilement enregistrer le nom et la profession dans des variables distinctes, et lorsque les deux variables sont enregistrées, créer notre employé. Seulement ici, avec le début de l'élément, nous n'avons pas de paramètre pour le texte à l'intérieur de l'élément. Nous devons utiliser des méthodes sur le texte. Mais comment obtenir des informations textuelles à l’intérieur d’un élément s’il s’agit de méthodes complètement différentes ? Ma solution : il suffit de retenir le nom du dernier élément, et charactersde vérifier dans quel élément nous lisons l'information. Vous devez également vous rappeler que <codee>characters lit tous les caractères à l'intérieur des éléments, ce qui signifie que tous les espaces et même les sauts de ligne seront lus. Et nous n'en avons pas besoin. Nous devons ignorer ces données car elles sont incorrectes.</codee> Code :
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("Name сотрудника: %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;
            }
        }
    }
}
Comme vous pouvez le constater, en raison de la complexité banale de la structure des fichiers XML, notre code est devenu beaucoup plus compliqué. Cependant, le code n’est pas compliqué. Description : nous avons créé des variables pour stocker des données sur l'employé ( name, job) , ainsi qu'une variable lastElementNamepour enregistrer dans quel élément nous nous trouvons. Après cela, dans la méthode characters, nous filtrons les informations, et s'il y a encore des informations, cela signifie que c'est le texte dont nous avons besoin, puis nous déterminons s'il s'agit d'un nom ou d'une profession en utilisant lastElementName. Dans la méthode endElement, on vérifie si toutes les informations ont été lues, et si c'est le cas, on crée un employé et on réinitialise les informations. Le résultat de la solution est équivalent au premier exemple :
Name сотрудника: Maksim, его должность: Middle Software Developer
Name сотрудника: Ivan, его должность: Junior Software Developer
Name сотрудника: Franklin, его должность: Junior Software Developer
Name сотрудника: Herald, его должность: Middle Software Developer
Name сотрудника: Adam, его должность: Middle Software Developer
Name сотрудника: Leroy, его должность: Junior Software Developer
Ainsi, ce problème a été résolu , mais vous pouvez remarquer que la complexité est plus élevée. Par conséquent, nous pouvons conclure que le stockage des informations textuelles dans des attributs sera le plus souvent plus correct que dans des éléments individuels. Et encore une tâche intéressante qui résoudra partiellement le problème dans JavaRush concernant l'affichage des informations sur un élément en HTML, seulement il faudra le modifier un peu, ici nous allons simplement lister tous les éléments à l'intérieur d'un élément :) Tâche n°3 - étant donné l'élément element, affichez les noms et attributs de tous les éléments internes ; si l'élément n'est pas trouvé, affichez-le. Pour cette tâche, nous utiliserons le fichier XML suivant :
<?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>
Comme vous pouvez le voir, nous avons ici trois scénarios possibles : root, mysql, oracle. Ensuite, le programme affichera toutes les informations sur tous les éléments à l'intérieur. Comment peut-on le faire? Et c'est assez simple : il suffit de déclarer une variable logique isEntered, qui indiquera si nous avons besoin de l'élément à l'intérieur, et s'il est à l'intérieur, lira toutes les données de startElement. Code de la solution :
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("Name атрибута: %s, его meaning: %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;
        }
    }
}
Dans ce code, lors de la saisie d'un élément sur lequel nous avons besoin d'informations, nous définissons l'indicateur isEnteredsur true, ce qui signifie que nous sommes à l'intérieur de l'élément. Et dès qu'on est à l'intérieur de l'élément, on traite simplement chaque nouvel élément startElement, sachant qu'il s'agit exactement d'un élément interne de notre élément. Nous affichons donc le nom de l’élément et son titre. Si l'élément n'a pas été trouvé dans le fichier, alors nous avons une variable isFoundqui est définie lorsque l'élément est trouvé, et si elle est fausse, un message s'affichera indiquant que l'élément n'a pas été trouvé. Et comme vous pouvez le voir, dans l’exemple SearchingXMLHandlernous avons passé rootun élément au constructeur. Conclusion pour lui :
Найден элемент <oracle>, его атрибуты:
Найден элемент <connection>, его атрибуты:
Name атрибута: value, его meaning: jdbc:oracle:thin:@10.220.140.48:1521:test1
Найден элемент <user>, его атрибуты:
Name атрибута: value, его meaning: secretOracleUsername
Найден элемент <password>, его атрибуты:
Name атрибута: value, его meaning: 111
Найден элемент <mysql>, его атрибуты:
Найден элемент <connection>, его атрибуты:
Name атрибута: value, его meaning: jdbc:mysql:thin:@10.220.140.48:1521:test1
Найден элемент <user>, его атрибуты:
Name атрибута: value, его meaning: secretMySQLUsername
Найден элемент <password>, его атрибуты:
Name атрибута: value, его meaning: 222
Ainsi, nous avons reçu toutes les informations sur les éléments internes et leurs attributs. Le problème est résolu. <h2>Épilogue</h2>Vous avez vu que SAX est un outil assez intéressant et assez efficace, et il peut être utilisé de différentes manières, à des fins différentes, etc., il vous suffit de regarder le problème depuis la droite. côté, comme le montrent les tâches n° 2 et n° 3, où SAX n'a ​​pas fourni de méthodes directes pour résoudre le problème, mais, grâce à notre ingéniosité, nous avons pu trouver une issue à la situation. La prochaine partie de l'article sera entièrement consacrée aux DOM. J'espère que vous avez aimé découvrir SAX. Expérimentez, pratiquez et vous comprendrez que tout est assez simple. Et c'est tout, bonne chance avec votre programmation et attendons avec impatience la partie sur le DOM bientôt. Bonne chance dans vos études :) Article précédent : [Concours] Bases XML pour un programmeur Java - Partie 2 sur 3 Article suivant : [Concours] Bases XML pour un programmeur Java - Partie 3.2 sur 3 - DOM
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION