JavaRush /Blogue Java /Random-PT /Noções básicas de XML para o programador Java - Parte 3.1...
Ярослав
Nível 40
Днепр

Noções básicas de XML para o programador Java - Parte 3.1 de 3 - SAX

Publicado no grupo Random-PT
Introdução Olá a todos os leitores do meu ainda não último artigo, e quero parabenizá-los: as coisas complicadas sobre XML ficaram para trás. Este artigo conterá código em Java. Haverá um pouco de teoria e depois prática. Devido ao fato de um material sobre SAX ocupar 10 páginas no Word, percebi que não poderia caber nos limites. Portanto, o artigo 3º será dividido em 3 artigos distintos, por mais estranho que possa parecer. Tudo estará nesta ordem: SAX -> DOM -> JAXB. Este artigo se concentrará apenas no SAX. PS Houve uma tarefa em algum lugar do curso onde era necessário exibir todos os elementos internos em um arquivo HTML. Após este artigo, você poderá fazer isso sem ler linha por linha com BufferedReaderalgoritmos de processamento convencionais e complexos, e também uma solução semelhante será dada no último exemplo prático. Vamos começar :) SAX (API simples para XML) - TEORIA O manipulador SAX é projetado de tal forma que simplesmente lê arquivos XML sequencialmente e reage a diferentes eventos, após o que passa as informações para um manipulador de eventos especial. Possui alguns eventos, mas os mais frequentes e úteis são os seguintes:
  1. startDocument- o início do documento
  2. endDocument- fim do documento
  3. startElement- abrindo um elemento
  4. endElement- fechar um elemento
  5. characters— informações de texto dentro dos elementos.
Todos os eventos são processados ​​em um manipulador de eventos , que deve ser criado e os métodos substituídos . Vantagens: alto desempenho devido ao método “direto” de leitura de dados, baixo custo de memória. Desvantagens: funcionalidade limitada, o que significa que em problemas não lineares teremos que refiná-la. SAX (Simple API for XML) – PRÁTICA Imediatamente uma lista de importações para que você não pesquise e confunda 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;
Agora, primeiro precisamos criar um SAXParser:
public class SAXExample {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException {
        // Creation фабрики и образца parserа
        SAXParserFactory factory = SAXParserFactory.newInstance();
        SAXParser parser = factory.newSAXParser();
    }
}
Como você pode ver, primeiro você precisa criar uma fábrica e, em seguida, criar o próprio analisador na fábrica. Agora que temos o analisador propriamente dito, precisamos de um manipulador para seus eventos. Para isso, precisamos de uma classe separada para nossa conveniência:
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 {
            // Тут будет логика реакции на пустое пространство внутри элементов (пробелы, переносы строчек и так далее).
        }
    }
}
Criamos uma classe com todos os métodos necessários para lidar com os eventos listados na teoria. Um pouco mais de teoria adicional: Um pouco sobre characters: ​​se o elemento contém texto, por exemplo, “ olá ”, então, teoricamente, o método pode ser chamado 5 vezes seguidas para cada caractere individual, mas isso não é assustador, pois tudo ainda funcionará. Sobre os métodos startElemente endElement:uri - este é o espaço no qual o elemento está localizado, localName- este é o nome do elemento sem prefixo, qName- este é o nome do elemento com um prefixo (se houver, caso contrário, apenas o nome do elemento). urie localNamesempre vazio se não habilitamos o processamento de espaço na fábrica. Isso é feito usando o método de fábrica setNamespaceAware(true). Então podemos obter espaço ( uri) e elementos com prefixos na frente deles ( localName). Tarefa nº 1 – Temos o seguinte 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>
Nosso objetivo: obter todas as informações sobre todos os funcionários deste arquivo. Primeiro, precisamos criar uma 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;
    }
}
E na nossa classe principal SAXExampleprecisamos de uma lista com todos os funcionários:
private static ArrayList<Employee> employees = new ArrayList<>();
Agora vamos examinar cuidadosamente onde estão as informações de que precisamos no arquivo XML. E, como podemos ver, toda a informação que precisamos são os atributos dos elementos employee. E como startElementtemos um parâmetro tão útil como attributes, temos uma tarefa bastante simples. Primeiro, vamos remover métodos desnecessários para não sobrecarregar nosso código. Precisamos apenas do startElement. E no próprio método devemos coletar informações dos atributos da tag do funcionário. Atenção:
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));
            }
        }
    }
}
A lógica é simples: se o nome de um elemento for employee, simplesmente receberemos informações sobre seus atributos. Existe attributesum método útil onde, sabendo o nome de um atributo, você pode obter seu valor. Foi isso que usamos. Agora que criamos um manipulador de eventos para o início de um elemento, precisamos analisar nosso arquivo XML . Para fazer isso, basta fazer o seguinte:
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));
            }
        }
    }
}
No método parse você deve passar o caminho para o arquivo xml e o manipulador que você criou. E assim, usando este código extraímos informações deste 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>
E obtivemos a seguinte saída:
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
Missão cumprida! Tarefa nº 2 – temos o seguinte 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>
Nosso objetivo: obter todas as informações sobre todos os funcionários deste arquivo. Este problema demonstrará bem como um arquivo XML mal estruturado pode dificultar a escrita de código. Como você pode ver, as informações sobre nome e posição agora são armazenadas como informações de texto dentro dos elementos namee job. Para ler texto dentro de elementos, temos o método de caracteres. Para fazer isso, precisamos criar uma nova classe de manipulador com lógica aprimorada. Não se esqueça de que os manipuladores são classes completas, capazes de armazenar lógica de qualquer complexidade. Portanto, agora vamos ajustar nosso processador. Na verdade, basta observar que sempre nos namerevezamos job, e não importa a ordem, podemos facilmente salvar o nome e a profissão em variáveis ​​separadas, e quando ambas as variáveis ​​forem salvas, criar nosso funcionário. Só aqui, junto com o início do elemento, não temos parâmetro para o texto dentro do elemento. Precisamos usar métodos em texto. Mas como obtemos informações de texto dentro de um elemento se esses métodos são completamente diferentes? Minha solução: só precisamos lembrar o nome do último elemento, e charactersverificar em qual elemento estamos lendo a informação. Você também precisa lembrar que <codee>characters lê todos os caracteres dentro dos elementos, o que significa que todos os espaços e até mesmo quebras de linha serão lidos. E não precisamos deles. Precisamos ignorar esses dados porque estão incorretos.</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("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;
            }
        }
    }
}
Como você pode ver, devido à complicação banal da estrutura do arquivo XML, nosso código tornou-se significativamente mais complicado. No entanto, o código não é complicado. Descrição: criamos variáveis ​​para armazenar dados sobre o funcionário ( name, job) , bem como uma variável lastElementNamepara registrar em qual elemento estamos. Depois disso, no método charactersfiltramos as informações, e se ainda houver informações aí, isso significa que este é o texto que precisamos, e então determinamos se é um nome ou uma profissão usando lastElementName. No método endElement, verificamos se todas as informações foram lidas, e se sim, criamos um funcionário e zeramos as informações. A saída da solução é equivalente ao primeiro exemplo:
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
Assim, esse problema foi resolvido , mas você pode perceber que a complexidade é maior. Portanto, podemos concluir que o armazenamento de informações textuais em atributos será, na maioria das vezes, mais correto do que em elementos individuais. E mais uma tarefa bacana que resolverá parcialmente o problema do JavaRush sobre a exibição de informações sobre um elemento em HTML, só que precisará ser editado um pouco, aqui simplesmente listaremos todos os elementos dentro de um elemento :) Tarefa nº 3 - dado o elemento elemento, exiba os nomes e atributos de todos os elementos internos; se o elemento não for encontrado, exiba isto. Para esta tarefa usaremos o seguinte arquivo 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 você pode ver, temos três cenários possíveis aqui: root, mysql, oracle. Em seguida, o programa exibirá todas as informações sobre todos os elementos internos. Como podemos fazer isso? E é bem simples: só precisamos declarar uma variável lógica isEntered, que indicará se precisamos do elemento dentro, e se estiver dentro, leremos todos os dados de startElement. Código da solução:
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;
        }
    }
}
Neste código, ao inserir um elemento sobre o qual precisamos de informações, configuramos o flag isEnteredcomo true, o que significa que estamos dentro do elemento. E assim que estivermos dentro do elemento, simplesmente processamos cada novo elemento startElement, sabendo que é exatamente um elemento interno do nosso elemento. Então, geramos o nome do elemento e seu título. Se o elemento não foi encontrado no arquivo, então temos uma variável isFoundque é definida quando o elemento é encontrado, e se for falsa, será exibida uma mensagem informando que o elemento não foi encontrado. E como você pode ver, no exemplo SearchingXMLHandlerpassamos rootum elemento para o construtor. Conclusão para ele:
Найден элемент <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
Assim, recebemos todas as informações sobre os elementos internos e seus atributos. O problema está resolvido. <h2>Epílogo</h2>Você viu que o SAX é uma ferramenta bastante interessante e bastante eficaz, e pode ser usada de diferentes maneiras, para diferentes propósitos, e assim por diante, basta olhar o problema da direita lado, conforme mostrado nas tarefas nº 2 e nº 3, onde o SAX não forneceu métodos diretos para resolver o problema, mas, graças à nossa engenhosidade, conseguimos encontrar uma saída para a situação. A próxima parte do artigo será inteiramente dedicada ao DOM. Espero que você tenha gostado de conhecer o SAX. Experimente, pratique e você entenderá que tudo é bem simples. E isso é tudo, boa sorte com sua programação e aguarde a parte sobre o DOM em breve. Boa sorte nos seus estudos :) Artigo anterior: [Competição] Noções básicas de XML para um programador Java - Parte 2 de 3 Próximo artigo: [Competição] Noções básicas de XML para um programador Java - Parte 3.2 de 3 - DOM
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION