Перейти к основному содержимому

Разбор XML-файла с помощью SAX Parser

· 5 мин. чтения

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 for XML .

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

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

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

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

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

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

<foreach>
<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>
</foreach>

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

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

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

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

  • characters(char[], int, int) получает символы с границами. Мы преобразуем их в строку и сохраним в переменной ForEachHandler.

  • startDocument() вызывается, когда начинается синтаксический анализ — мы будем использовать его для создания нашего экземпляра ForEach.

  • startElement() вызывается, когда начинается синтаксический анализ элемента — мы будем использовать его для создания экземпляров List<ForEachArticle> или ForEachArticleqName помогает нам различать оба типа

  • endElement() вызывается, когда синтаксический анализ элемента заканчивается — это когда мы назначаем содержимое тегов их соответствующим переменным.

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

public class ForEachHandler 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 ForEach 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 ForEach();
}

@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 ForEachArticle());
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 ForEachArticle latestArticle() {
List<ForEachArticle> articleList = website.articleList;
int latestArticleIndex = articleList.size() - 1;
return articleList.get(latestArticleIndex);
}

public ForEach getWebsite() {
return website;
}
}

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

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

6. Тестирование парсера

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

SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
SaxParserMain.ForEachHandler foreachHandler = new SaxParserMain.ForEachHandler();

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

saxParser.parse("src/test/resources/sax/foreach.xml", foreachHandler);

SaxParserMain.ForEach result = foreachHandler.getWebsite();

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

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

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

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

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

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

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

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

Как обычно, код для этой статьи доступен на GitHub .