«1. Обзор

SAX, также известный как Simple API for XML, используется для разбора XML-документов.

В этом уроке мы узнаем, что такое SAX и почему, когда и как его следует использовать.

2. SAX: простой API для XML

SAX — это API, используемый для разбора XML-документов. Он основан на событиях, генерируемых при чтении документа. Методы обратного вызова получают эти события. Пользовательский обработчик содержит эти методы обратного вызова.

Этот API эффективен, потому что он отбрасывает события сразу после их получения обратными вызовами. Поэтому SAX имеет эффективное управление памятью, в отличие, например, от DOM.

3. SAX против DOM

DOM означает объектную модель документа. Парсер DOM не полагается на события. Более того, он загружает весь XML-документ в память для его анализа. SAX более эффективно использует память, чем DOM.

У DOM тоже есть свои преимущества. Например, DOM поддерживает XPath. Это также упрощает работу сразу со всем деревом документов, поскольку документ загружается в память.

4. SAX против StAX

StAX более новый, чем SAX и DOM. Это означает Streaming API для XML.

Основное отличие от SAX заключается в том, что StAX использует механизм вытягивания вместо механизма выталкивания SAX (с использованием обратных вызовов). Это означает, что контроль предоставляется клиенту, чтобы решить, когда события должны быть извлечены. Таким образом, нет обязанности вытягивать весь документ, если нужна только его часть.

Он предоставляет простой API для работы с XML с эффективным использованием памяти способом синтаксического анализа.

В отличие от SAX, он не обеспечивает проверку схемы в качестве одной из своих функций.

5. Разбор XML-файла с помощью пользовательского обработчика

Давайте теперь воспользуемся следующим XML-кодом, представляющим веб-сайт Baeldung и его статьи:

<baeldung>
    <articles>
        <article>
            <title>Parsing an XML File Using SAX Parser</title>
            <content>SAX Parser's Lorem ipsum...</content>
        </article>
        <article>
            <title>Parsing an XML File Using DOM Parser</title>
            <content>DOM Parser's Lorem ipsum...</content>
        </article>
        <article>
            <title>Parsing an XML File Using StAX Parser</title>
            <content>StAX's Lorem ipsum...</content>
        </article>
    </articles>
</baeldung>

Мы начнем с создания POJO для нашего корневого элемента Baeldung и его Children:

public class Baeldung {
    private List<BaeldungArticle> articleList;
    // usual getters and setters
}
public class BaeldungArticle {
    private String title;
    private String content;
    // usual getters and setters
}

Мы продолжим создание BaeldungHandler. Этот класс будет реализовывать методы обратного вызова, необходимые для захвата событий.

    Мы переопределим четыре метода из суперкласса DefaultHandler, каждый из которых характеризует событие:

characters(char[], int, int) получает символы с границами. Мы преобразуем их в строку и сохраним в переменной BaeldungHandler startDocument() вызывается, когда начинается синтаксический анализ — мы будем использовать его для создания экземпляра Baeldung. – мы будем использовать его для создания экземпляров List\u003cBaeldungArticle\u003e или BaeldungArticle – qName помогает нам различать оба типа endElement() вызывается, когда синтаксический анализ элемента заканчивается – это когда мы Назначим содержимое тегов соответствующим переменным

public class BaeldungHandler extends DefaultHandler {
    private static final String ARTICLES = "articles";
    private static final String ARTICLE = "article";
    private static final String TITLE = "title";
    private static final String CONTENT = "content";

    private Baeldung website;
    private StringBuilder elementValue;

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        if (elementValue == null) {
            elementValue = new StringBuilder();
        } else {
            elementValue.append(ch, start, length);
        }
    }

    @Override
    public void startDocument() throws SAXException {
        website = new Baeldung();
    }

    @Override
    public void startElement(String uri, String lName, String qName, Attributes attr) throws SAXException {
        switch (qName) {
            case ARTICLES:
                website.articleList = new ArrayList<>();
                break;
            case ARTICLE:
                website.articleList.add(new BaeldungArticle());
                break;
            case TITLE:
                elementValue = new StringBuilder();
                break;
            case CONTENT:
                elementValue = new StringBuilder();
                break;
        }
    }

    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        switch (qName) {
            case TITLE:
                latestArticle().setTitle(elementValue.toString());
                break;
            case CONTENT:
                latestArticle().setContent(elementValue.toString());
                break;
        }
    }

    private BaeldungArticle latestArticle() {
        List<BaeldungArticle> articleList = website.articleList;
        int latestArticleIndex = articleList.size() - 1;
        return articleList.get(latestArticleIndex);
    }

    public Baeldung getWebsite() {
        return website;
    }
}

Теперь, когда определены все обратные вызовы, мы можем написать класс BaeldungHandler:

Для повышения удобочитаемости также были добавлены строковые константы. Также удобен метод получения последней обнаруженной статьи. Наконец, нам нужен геттер для объекта Baeldung.

Обратите внимание, что приведенное выше не является потокобезопасным, поскольку мы удерживаем состояние между вызовами метода.

6. Тестирование синтаксического анализатора

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
SaxParserMain.BaeldungHandler baeldungHandler = new SaxParserMain.BaeldungHandler();

Чтобы протестировать синтаксический анализатор, мы создадим SaxFactory, SaxParser, а также BaeldungHandler:

saxParser.parse("src/test/resources/sax/baeldung.xml", baeldungHandler);

SaxParserMain.Baeldung result = baeldungHandler.getWebsite();

assertNotNull(result);
List<SaxParserMain.BaeldungArticle> articles = result.getArticleList();

assertNotNull(articles);
assertEquals(3, articles.size());

SaxParserMain.BaeldungArticle articleOne = articles.get(0);
assertEquals("Parsing an XML File Using SAX Parser", articleOne.getTitle());
assertEquals("SAX Parser's Lorem ipsum...", articleOne.getContent());

SaxParserMain.BaeldungArticle articleTwo = articles.get(1);
assertEquals("Parsing an XML File Using DOM Parser", articleTwo.getTitle());
assertEquals("DOM Parser's Lorem ipsum...", articleTwo.getContent());

SaxParserMain.BaeldungArticle articleThree = articles.get(2);
assertEquals("Parsing an XML File Using StAX Parser", articleThree.getTitle());
assertEquals("StAX Parser's Lorem ipsum...", articleThree.getContent());

После этого мы проанализируем файл XML и выполним утверждение что объект содержит все ожидаемые проанализированные элементы:

Как и ожидалось, baeldung был правильно проанализирован и содержит ожидаемые подобъекты.

7. Заключение

Мы только что узнали, как использовать SAX для разбора файлов XML. Это мощный API, занимающий мало памяти в наших приложениях.