Java — Парсинг xml-переменной через SAX на java


Простой SAX парсер на Java

категория
Java
дата 11.08.2009
автор MUTOgen
голосов 30

Во многих задачах возникает необходимость использования разного рода xml файлов в различных целях. Я не буду пытаться объять необъятное, а расскажу по своему опыту для чего мне все это понадобилось.

Java, пожалуй, мой самый любимый язык программирования. К тому же эта любовь укрепляется тем, что решить можно любую задачу и придумывать велосипеда не придется.

Так вот, понадобилось мне создать такую связку клиент-сервер, работающую с БД, которая бы позволяла клиенту удаленно вносить записи в БД сервера. Само собой должны быть проверки вводимых данных и т.д. и т.п., но речь не об этом.

В качестве принципа работы я, не долго думая, избрал передачу информации в виде xml файла. Вида следующего:

Чтобы проще было читать дальше, скажу лишь что это информация о врачах учреждений. Фамилия, имя, отчество, уникальных id, и так далее. В общем ряд данных. Далее этот файл благополучно попадал на серверную сторону, и тут начинался разбор файла.

Из двух вариантов разбора(SAX vs DOM) я выбрал SAX ввиду того, что он пошустрее работает, и он первым мне попался в руки :)

Итак. Как известно, для успешной работы с парсером нам необходимо переопределить нужные нам методы DefaultHandler’а. Для начала подключим необходимые пакеты.

Теперь можно начать писать наш парсер

Начнем с метода startDocument(). Он, как понятно из названия, реагирует на событие начала документа. Сюда можно повесить различные действия по выделению памяти например или по сбросу значений, но наш пример довольно прост, поэтому просто обозначим начало работы соотвествующим сообщением:

Далее. Парсер идет по документу, встречает элемент его структуры. Начинает работать метод startElement(). Причем на самом деле вид его такой: startElement(String namespaceURI, String localName, String qName, Attributes atts). Здесь namespaceURI — это пространство имен, localName — локальное имя элемента, qName- это комбинация локального имени с пространством имен (разделяется двоеточием) и atts — атрибуты данного элемента. В нашем случае все просто. Достаточно воспользоваться qName’ом и кинуть его в некоторую служебную строку thisElement. Тем самым мы помечаем в каком элементе в данный момент мы находимся.

Далее, встретив элемент мы доходим до его значения. Здесь включается метод characters(). Он имеет вид: characters(char[] ch, int start, int length). Ну тут все понятно. ch — это массив содержащий собственной саму строку-значение внутри данного элемента. start и length — служебные числа обозначающие точку старта в строке и длину.

Ах, да. Чуть не забыл. В качестве объекта куда буду скидываться напарсенные данные выступает объект типа Doctors. Этот класс определен и имеет все необходимые сеттеры-геттеры.

Далее очевидно элемент заканчивается и за ним идет следующий. За окончание отвечает endElement(). Она сигнализирует нам что элемент закончился и можно что-нибудь сделать в это время. Так и поступим. Очистим Element.

Пройдя таким образом весь документ, мы придем к концу файла. Сработает endDocument(). В нем мы можем высвободить память, сделать какую-то диагностичесую печать и т.д. В нашем случае просто напишем о том что парсинг заканчивается.

Таким образом мы получили класс для парсинга xml нашего формата. Вот полный текст:

Надеюсь топик помог легко представить суть работы SAX парсера.

Не судите строго первую статью:) Надеюсь она была хоть кому-то полезна.

UPD: Чтобы запустить данный парсер, можно воспользоваться таким кодом:

Если Вам понравилась статья, проголосуйте за нее

Чтение XML файла средствами SAX XML

В отличие от DOM парсера, SAX парсер не читает весь XML файл, не загружает его полностью в оперативную память и не создает полную объектную модель читаемого XML документа. Вместо этого, SAX парсер информирует клиентский код о структуре читаемого XML документ, вызывая методы класса org.xml.sax.helpers.DefaultHandler .

SAX парсер работает намного быстрее и использует гораздо меньше памяти, чем DOM XML парсер.

Для того, чтобы сообщить клиентскому коду структуру и содержание XML документа, SAX парсер использует следующие методы:


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

На самом деле, в классе определено гораздо больше методов, но в данной статье они не представляют для нас интереса. Для получения информации о них обратитесь к документации.

Мы рассмотрим простой пример использования этих методов для получения структуры XML файла.

XML документ — DOM, SAXParser

Существуют две стратегии обработки XML документов: DOM (Document Object Model) и SAX (Simple API for XML). Основное их отличие связано с тем, что использование DOM позволяет читать и вносить изменения в существующий XML-документ, а также создавать новый. Стратегия использования SAX основывается на том, что содержимое XML-документа только анализируется. XML-текст может быть больших размеров: DOM должен весь документ «заглотить» и проанализировать, а SAX-парсер обрабатывает XML-документ последовательно и не требует дополнительной памяти.

Для работы с XML-файлами Java располагает достаточно большим набором инструментов, начиная от встроенных возможностей, которые предоставляет Core Java, и заканчивая большим набором разнообразного стороннего кода, оформленного в виде библиотек. Сначала рассмотрим использование DOM для чтения XML-файла и создания нового файла/документа. А в заключение будет приведено описание и применение SAX-парсера SAXParser.

XML документ представляет собой набор узлов (тегов). Каждый узел может иметь неограниченное количество дочерних узлов, которые, в свою очередь, также могут содержать потомков или не содержать их совсем. Таким образом строится дерево объектов. DOM — это объектная модель документа, которая представляет собой это дерево в виде специальных объектов/узлов org.w3c.dom.Node. Каждый узел Node соответствует своему XML-тегу и содержит полную информацию о том, что это за тег, какие он имеет атрибуты, какие дочерние узлы содержит внутри себя и т.д. На самой вершине этой иерархии находится org.w3c.dom.Document, который является корневым элементов дерева.

Чтение XML-файла

Получить объект Document XML-файла можно следующим образом :

Чтобы найти какой-либо узел в дереве можно использовать метод getElementsByTagName, который возвращает список всех элементов :

Метод getElementsByTagName является case-sensitive, т.е. различает прописные и строчные символы.

В цикле можно просмотреть все дочерние узлы. C помощью метода getAttributes можно узнать атрибуты узла. Метод getNodeType позволяет проверить тип узла :

Создание XML-файла

Для создания нового объекта Document используйте следующий код :

Элемент Element объекта Document создается с использованием метода createElement. Для определения значения элемента следует использовать метод setTextContent. Для добавления элемента в узловую запись используйте метод appendChild (Node). Элемент может содержать атрибуты. Чтобы добавить к элементу атрибут следует использовать метод setAttribute. Если элемент уже содержит атрибут, то его значение изменится.

В результате работы примера будет создан Document следующей структуры :

Пример чтения и создания XML-файла

Для чтения готового XML-файла и формирования нового файла создадим в IDE Eclipse простой проект XMLSample, структура которого представлена на следующем скриншоте.

Проект включает XML-файл «posts.xml» с исходными данными, создаваемый XML-файл данных «data.xml», класс Post.java, в который будут упаковываться отдельные записи массива данных и основной класс проекта XMLSample, который будет производить все необходимые действия.

Структура XML-файла

В качестве исходных данных используется XML-файл «posts.xml» из примеров разработчиков Sencha GXT 3.1.1. Структура XML-данных содержит корневой элемент и набор объектов/сущностей, представленных тегами .

Листинг класса Person

Класс Person имеет несколько полей. Идентификатор записи id определяется при создании объекта в конструкторе. Методы set/get не представлены в листинге.

Чтение XML-файла


Для чтения XML-файла в проекте используется метод readDataXML(), который создает список persons типа List

, читает XML-файл данных и формирует объект doc типа Document. После этого в цикле создается массив данных. Вспомогательная функция getValue извлекает текст атрибута записи.

Следует обратить внимание, что для чтения значения атрибута записи (объекта Person) сначала получаем ссылку на массив тегов , и после этого по индексу в функции getValue извлекаем значение.

Создание XML-файла

Для создания нового XML-файла на основе массива posts подготовим два списка данных типа List : пользователей users и форумов forums. Эти два массива запишем в XML-файл.

Создание нового объекта Document и сохранение его в XML-файл в проекте выполняет метод writeDataXML :

Листинг метода создания XML-файла

Процедура сохранения объекта Document в XML-файл представлена отдельным методом writeDocument :

Листинг процедуры сохранения XML-файла

Если массив posts окажется пустым, то новый XML-файл должен будет иметь следующий вид :

SAX-парсер, SAXParser

SAX-парсеры используют для анализа XML-строки или извлечения из нее необходимой информации. Обычно SAX-парсеры требуют фиксированный объем памяти и не позволяют изменять содержимое. Для связи SAX-парсера с вызывающим приложением, как правило, используется функция обратного вызова.

Рассмотрим пример SAXExample.java с использованием класса SAXParser для анализа XML-текста, представленного файлом phonebook.xml, содержащего 3 записи и имеющего следующий вид :

Файл phonebook.xml

Пакеты «javax.xml.parsers» и «org.xml.sax» включают набор классов для «разбора» XML в строковом представлении. К основным классам этих пакетов, с точки зрения разложения XML объекта на составляющие, относятся SAXParser и DefaultHandler.

В примере SAXExample.java создается класс handler типа DefaultHandler, в котором методы анализа XML-строки переопределяются. Все прозрачно.

Листинг SAXExample.java

В результате выполнения примера использования SAXParser в консоль будут выведены следующие сообщения :

Скачать примеры

Рассмотренные на странице примеры использования DOM для чтения и создания XML-документа, применения SAXParser’а для анализа XML-текста в виде проекта Eclipse можно скачать здесь (133 Кб).

Java — Парсинг xml-переменной через SAX на java

Для того, чтобы использовать SAX парсер, нужно определить класс, расширяющий DefaultHandler и переопределить некоторые из его методов. Именно этот класс и определяет формат выходных данных:

Цукерберг рекомендует:  Коллекции в Java

SAX парсер работает в данном случае так — когда в исходном документе встречается открывающий тег, вызывается метод startElement с соответствующими параметрами. При чтении содержимого тега вызывается characters. При закрытии тега — endElement. Таким образом документ и разбирается без предварительной его загрузки в память. Для использования DOM парсера просто создадим класс, работающий с объектом типа Document и определяющим формат выходных данных. Здесь никаких событий нет, есть уже загруженный объект типа Document, который является представлением исходных данных.

Теперь осталось только загрузить исходные данные и использовать реализованные парсеры:

Результат парсинга для первого и второго вариантов выглядит аналогично:


Какой парсер лучше использовать в вашем случае — SAX или DOM, можно решить по типу исходных данных. Если исходный документ большой по объему и вложенность элементов невысока — скорее всего SAX, при этом подходе разбор исходных данных происходит несколько быстрее и используется меньше памяти. Если нужно преобразовать сложную структуру данных — наверное удобнее и проще будет работать с DOM.

Java SAX Parsing

13 Haji [2011-12-22 12:42:00]

Есть поток XML, который мне нужно проанализировать. Поскольку мне нужно только сделать это один раз и построить мои объекты Java, SAX выглядит как естественный выбор. Я расширяю DefaultHandler и реализую методы startElement, endElement и characters, имея членов в моем классе, где я сохраняю текущее значение чтения (взятое в методе символов).

У меня нет проблем с тем, что мне нужно, но мой код стал довольно сложным, и я уверен, что нет причин для этого и что я могу делать что-то по-другому. Структура моего XML выглядит примерно так:

Моя проблема началась, когда я понял, что те же имена тегов используются в нескольких областях файла. Например, id и name существуют как для игрока, так и для команды. Я хочу создать экземпляры моих классов java Player и Team. Во время разбора я сохранил логические флаги, говорящие мне, есть ли я в разделе команд, чтобы в конце этого элемента я узнал, что имя — это имя команды, а не имя игрока и т.д.

Вот как выглядит мой код:

Поскольку в моем реальном сценарии у меня есть больше узлов для игрока в дополнение к командам, и эти узлы также имеют теги, такие как имя и идентификатор, я обнаружил, что меня перепутали с несколькими булерами, подобными inTeamsSection, и мой метод endElement становится длинным и комплекс со многими условиями.

Что мне делать по-другому? Как я могу узнать, что, например, тег имени принадлежит?

5 ответов

20 Решение Jörn Horstmann [2011-12-22 15:08:00]

При написании SAX-анализатора существует один аккуратный трюк: разрешено изменять ContentHandler XMLReader во время разбора. Это позволяет отделить логическая разборка для разных элементов в несколько классов, что делает синтаксический анализ более модульного и многоразового использования. Когда один обработчик видит свой конечный элемент, переключается обратно на родителя. Сколько обработчиков, которые вы реализуете, будет вы. Код будет выглядеть так:

Трудно советовать, не зная больше о ваших требованиях, но тот факт, что вы удивлены тем, что «мой код стал довольно сложным», говорит о том, что вы не были хорошо осведомлены, когда выбрали SAX. SAX — это низкоуровневый программный интерфейс, способный к очень высокой производительности, но это потому, что парсер делает гораздо меньше работы для вас, и поэтому вам нужно сделать гораздо больше работы самостоятельно.

Я настоятельно рекомендую прекратить парсинг самостоятельно и захватить хорошую библиотеку привязки XML-данных. XStream (http://x-stream.github.io/) может быть личным фаворитом, но там много разных библиотек. Он может быть даже способен анализировать ваши POJO на месте, без какой-либо конфигурации (если вы используете имена свойств и плюрализацию для соответствия структуре XML).

Я делаю что-то очень похожее, но вместо того, чтобы иметь флаги boolean , чтобы сообщить мне, в каком состоянии я вхожу, я тестирую player или team как null . Делает вещи немного аккуратнее. Это требует, чтобы вы установили их в null , когда вы обнаруживаете конец каждого элемента, после того как вы добавили его в соответствующий список.

Если вам нужен более красивый код, используйте StAX, это сравнение всех XML-синтаксических API говорит о том, что StAX — намного лучший вариант.

Производительность StAX в большинстве тестов лучше, чем в любой другой реализации API.

Поэтому я лично не вижу никаких оснований для продолжения работы SAX, если вы не выполняете некоторые связанные с устаревшим программированием.

How to read XML file in Java – (SAX Parser)

SAX parser is working differently with a DOM parser, it neither load any XML document into memory nor create any object representation of the XML document. Instead, the SAX parser use callback function ( org.xml.sax.helpers.DefaultHandler ) to informs clients of the XML document structure.

See following SAX callback methods :

  • startDocument() and endDocument() – Method called at the start and end of an XML document.
  • startElement() and endElement() – Method called at the start and end of a document element.
  • characters() – Method called with the text contents in between the start and end tags of an XML document element.

Читаем XML файл на Java с помощью JDOM Parser


JDOM предоставляет удобный и довольно простой API для обработки XML файлов, а именно чтения, записи и редактирования XML файлов. В этой статье мы рассмотрим считывание уже существующего XML файла в Java объект.

JDOM Parser не является частью стандартного JDK, поэтому для использования JDOM нужно будет скачать Jar файл с официального сайта и добавить его в проект. Я использую maven, поэтому просто добавлю в зависимости следующий код:

JDOM предоставляет четыре класса-обертки, с помощью которых мы можем получить объект JDOM Document. Сам объект JDOM Document содержит множество методов для получения корневого элемента, списка дочерних элементов, значений атрибутов и значения элемента по имени.

JDOM предоставляет следующие классы:

  1. Класс org.jdom2.input.DOMBuilder использует DOM Parser для разбора XML и преобразования его в JDOM Document .
  2. Класс org.jdom2.input.SAXBuilder использует SAX Parser для разбора XML и преобразования его в JDOM Document .
  3. Класс org.jdom2.input.StAXEventBuilder использует STAX Event Parser для разбора XML и преобразования его в JDOM Document .
  4. Класс org.jdom2.input.StAXStreamBuilder использует STAX Stream Parser для разбора XML и преобразования его в JDOM Document.
  5. Класс org.jdom2.Document содержит наиболее используемые методы для работы с XML: получения корневого элемента, считывания данных, редактирования и записи содержимого элементов.
  6. Класс org.jdom2.Element содержит другие часто используемые методы для получения списка дочерних элементов, значения дочерних элементов и значений атрибутов.

Пример чтения XML файла с помощью JDOM Parser

Ниже представлена программа для чтения XML файла в список объектов с помощью JDOM Parser.

Parsing an XML File Using SAX

In real-life applications, you will want to use the SAX parser to process XML data and do something useful with it. This section examines an example JAXP program, SAXLocalNameCount, that counts the number of elements using only the localName component of the element, in an XML document. Namespace names are ignored for simplicity. This example also shows how to use a SAX ErrorHandler.

Note — After you have downloaded and installed the sources of the JAXP API from the JAXP download area, the sample program for this example is found in the directory install-dir/jaxp-1_4_2-release-date/samples/sax. The XML files it interacts with are found in install-dir/jaxp-1_4_2-release-date/samples/data.

Creating the Skeleton

The SAXLocalNameCount program is created in a file named SAXLocalNameCount.java.

Because you will run it standalone, you need a main() method. And you need command-line arguments so that you can tell the application which file to process.

Importing Classes

The import statements for the classes the application will use are the following.

The javax.xml.parsers package contains the SAXParserFactory class that creates the parser instance used. It throws a ParserConfigurationException if it cannot produce a parser that matches the specified configuration of options. (Later, you will see more about the configuration options). The javax.xml.parsers package also contains the SAXParser class, which is what the factory returns for parsing. The org.xml.sax package defines all the interfaces used for the SAX parser. The org.xml.sax.helpers package contains DefaultHandler, which defines the class that will handle the SAX events that the parser generates. The classes in java.util and java.io, are needed to provide hash tables and output.

Setting Up I/O

The first order of business is to process the command-line arguments, which at this stage only serve to get the name of the file to process. The following code in the main method tells the application what file you want SAXLocalNameCountMethod to process.


This code sets the main method to throw an Exception when it encounters problems, and defines the command-line options which are required to tell the application the name of the XML file to be processed. Other command line arguments in this part of the code will be examined later in this lesson, when we start looking at validation.

The filename String that you give when you run the application will be converted to a java.io.File URL by an internal method, convertToFileURL(). This is done by the following code in SAXLocalNameCountMethod.

If the incorrect command-line arguments are specified when the program is run, then the SAXLocalNameCount application’s usage() method is invoked, to print out the correct options onscreen.

Further usage() options will be examined later in this lesson, when validation is addressed.

Implementing the ContentHandler Interface

The most important interface in SAXLocalNameCount is ContentHandler. This interface requires a number of methods that the SAX parser invokes in response to various parsing events. The major event-handling methods are: startDocument, endDocument, startElement, and endElement.

The easiest way to implement this interface is to extend the DefaultHandler class, defined in the org.xml.sax.helpers package. That class provides do-nothing methods for all the ContentHandler events. The example program extends that class.

Note — DefaultHandler also defines do-nothing methods for the other major events, defined in the DTDHandler, EntityResolver, and ErrorHandler interfaces. You will learn more about those methods later in this lesson.

Цукерберг рекомендует:  Android - AndroidManifest.xml, host=m.vk.com, pathPrefix=

Each of these methods is required by the interface to throw a SAXException. An exception thrown here is sent back to the parser, which sends it on to the code that invoked the parser.

Handling Content Events

This section shows the code that processes the ContentHandler events.

When a start tag or end tag is encountered, the name of the tag is passed as a String to the startElement or the endElement method, as appropriate. When a start tag is encountered, any attributes it defines are also passed in an Attributes list. Characters found within the element are passed as an array of characters, along with the number of characters (length) and an offset into the array that points to the first character.

Document Events

The following code handles the start-document and end-document events:

This code defines what the application does when the parser encounters the start and end points of the document being parsed. The ContentHandler interface’s startDocument() method creates a java.util.Hashtable instance, which in Element Events will be populated with the XML elements the parser finds in the document. When the parser reaches the end of the document, the endDocument() method is invoked, to get the names and counts of the elements contained in the hash table, and print out a message onscreen to tell the user how many incidences of each element were found.

Both of these ContentHandler methods throw SAXExceptions. You will learn more about SAX exceptions in Setting up Error Handling.

Element Events

As mentioned in Document Events, the hash table created by the startDocument method needs to be populated with the various elements that the parser finds in the document. The following code processes the start-element and end-element events:

This code processes the element tags, including any attributes defined in the start tag, to obtain the namespace universal resource identifier (URI), the local name and the qualified name of that element. The startElement() method then populates the hash map created by startDocument() with the local names and the counts thereof, for each type of element. Note that when the startElement() method is invoked, if namespace processing is not enabled, then the local name for elements and attributes could turn out to be an empty string. The code handles that case by using the qualified name whenever the simple name is an empty string.

Character Events

The JAXP SAX API also allows you to handle the characters that the parser delivers to your application, using the ContentHandler.characters() method.

Note — Character events are not demonstrated in the SAXLocalNameCount example, but a brief description is included in this section, for completeness.

Parsers are not required to return any particular number of characters at one time. A parser can return anything from a single character at a time up to several thousand and still be a standard-conforming implementation. So if your application needs to process the characters it sees, it is wise to have the characters() method accumulate the characters in a java.lang.StringBuffer and operate on them only when you are sure that all of them have been found.

You finish parsing text when an element ends, so you normally perform your character processing at that point. But you might also want to process text when an element starts. This is necessary for document-style data, which can contain XML elements that are intermixed with text. For example, consider this document fragment:

The initial text, This paragraph contains, is terminated by the start of the element. The text important is terminated by the end tag, , and the final text, ideas., is terminated by the end tag,


To be strictly accurate, the character handler should scan for ampersand characters (&) and left-angle bracket characters ( &entityName;

When you are handling large blocks of XML or HTML that include many special characters, you can use a CDATA section. A CDATA section works like . in HTML, only more so: all white space in a CDATA section is significant, and characters in it are not interpreted as XML. A CDATA section starts with .

An example of a CDATA section, taken from the sample XML file install-dir/jaxp-1_4_2-release-date/samples/data/REC-xml-19980210.xml, is shown below.

CDATA sections may occur anywhere character data may occur; they are used to escape blocks of text containing characters which would otherwise be recognized as markup. CDATA sections begin with the string » » and end with the string » ]]> «

Once parsed, this text would be displayed as follows:

CDATA sections may occur anywhere character data may occur; they are used to escape blocks of text containing characters which would otherwise be recognized as markup. CDATA sections begin with the string » «.

The existence of CDATA makes the proper echoing of XML a bit tricky. If the text to be output is not in a CDATA section, then any angle brackets, ampersands, and other special characters in the text should be replaced with the appropriate entity reference. (Replacing left angle brackets and ampersands is most important, other characters will be interpreted properly without misleading the parser.) But if the output text is in a CDATA section, then the substitutions should not occur, resulting in text like that in the earlier example. In a simple program such as our SAXLocalNameCount application, this is not particularly serious. But many XML-filtering applications will want to keep track of whether the text appears in a CDATA section, so that they can treat special characters properly.

Setting up the Parser

The following code sets up the parser and gets it started:

These lines of code create a SAXParserFactory instance, as determined by the setting of the javax.xml.parsers.SAXParserFactory system property. The factory to be created is set up to support XML namespaces by setting setNamespaceAware to true, and then a SAXParser instance is obtained from the factory by invoking its newSAXParser() method.

Note — The javax.xml.parsers.SAXParser class is a wrapper that defines a number of convenience methods. It wraps the (somewhat less friendly) org.xml.sax.Parser object. If needed, you can obtain that parser using the getParser() method of the SAXParser class.

You now need to implement the XMLReader that all parsers must implement. The XMLReader is used by the application to tell the SAX parser what processing it is to perform on the document in question. The XMLReader is implemented by the following code in the main method.

Here, you obtain an XMLReader instance for your parser by invoking your SAXParser instance’s getXMLReader() method. The XMLReader then registers the SAXLocalNameCount class as its content handler, so that the actions performed by the parser will be those of the startDocument(), startElement(), and endDocument() methods shown in Handling Content Events. Finally, the XMLReader tells the parser which document to parse by passing it the location of the XML file in question, in the form of the File URL generated by the convertToFileURL() method defined in Setting Up I/O.

Setting up Error Handling

You could start using your parser now, but it is safer to implement some error handling. The parser can generate three kinds of errors: a fatal error, an error, and a warning. When a fatal error occurs, the parser cannot continue. So if the application does not generate an exception, then the default error-event handler generates one. But for nonfatal errors and warnings, exceptions are never generated by the default error handler, and no messages are displayed.

As shown in Document Events, the application’s event handling methods throw SAXException. For example, the signature of the startDocument() method in the ContentHandler interface is defined as returning a SAXException.

public void startDocument() throws SAXException < /* . */ >

A SAXException can be constructed using a message, another exception, or both.

Because the default parser only generates exceptions for fatal errors, and because the information about the errors provided by the default parser is somewhat limited, the SAXLocalNameCount program defines its own error handling, through the MyErrorHandler class.

In the same way as in Setting up the Parser, which showed the XMLReader being pointed to the correct content handler, here the XMLReader is pointed to the new error handler by calling its setErrorHandler() method.

The MyErrorHandler class implements the standard org.xml.sax.ErrorHandler interface, and defines a method to obtain the exception information that is provided by any SAXParseException instances generated by the parser. This method, getParseExceptionInfo(), simply obtains the line number at which the error occurs in the XML document and the identifier of the system on which it is running by calling the standard SAXParseException methods getLineNumber() and getSystemId(). This exception information is then fed into implementations of the basic SAX error handling methods error(), warning(), and fatalError(), which are updated to send the appropriate messages about the nature and location of the errors in the document.

Handling NonFatal Errors

A nonfatal error occurs when an XML document fails a validity constraint. If the parser finds that the document is not valid, then an error event is generated. Such errors are generated by a validating parser, given a document type definition (DTD) or schema, when a document has an invalid tag, when a tag is found where it is not allowed, or (in the case of a schema) when the element contains invalid data.

The most important principle to understand about nonfatal errors is that they are ignored by default. But if a validation error occurs in a document, you probably do not want to continue processing it. You probably want to treat such errors as fatal.


To take over error handling, you override the DefaultHandler methods that handle fatal errors, nonfatal errors, and warnings as part of the ErrorHandler interface. As shown in the code extract in the previous section, the SAX parser delivers a SAXParseException to each of these methods, so generating an exception when an error occurs is as simple as throwing it back.

Цукерберг рекомендует:  Javascript - Чем отличается Java от JavaScript

Note — It can be instructive to examine the error-handling methods defined in org.xml.sax.helpers.DefaultHandler. You will see that the error() and warning() methods do nothing, whereas fatalError() throws an exception. Of course, you could always override the fatalError() method to throw a different exception. But if your code does not throw an exception when a fatal error occurs, then the SAX parser will. The XML specification requires it.

Handling Warnings

Warnings, too, are ignored by default. Warnings are informative and can only be generated in the presence of a DTD or schema. For example, if an element is defined twice in a DTD, a warning is generated. It is not illegal, and it does not cause problems, but it is something you might like to know about because it might not have been intentional. Validating an XML document against a DTD will be shown in the section .

Running the SAX Parser Example without Validation

As stated at the beginning of this lesson, after you have downloaded and installed the sources of the JAXP API from the JAXP sources download area, the sample program and the associated files needed to run it are found in the following locations.

The different Java archive (JAR) files for the example are located in the directory install-dir/jaxp-1_4_2-release-date/lib.

The SAXLocalNameCount.java file is found in install-dir/jaxp-1_4_2-release-date/samples/sax.

The XML files that SAXLocalNameCount interacts with are found in install-dir/jaxp-1_4_2-release-date/samples/data.

The following steps explain how to run the SAX parser example without validation.

To Run the SAXLocalNameCount Example without Validation

  1. Navigate to the samples directory. % cd install-dir/jaxp-1_4_2-release-date/samples.
  2. Compile the example class. % javac sax/*
  3. Run the SAXLocalNameCount program on an XML file.

Choose one of the XML files in the data directory and run the SAXLocalNameCount program on it. Here, we have chosen to run the program on the file rich_iii.xml.

% java sax/SAXLocalNameCount data/rich_iii.xml

The XML file rich_iii.xml contains an XML version of William Shakespeare’s play Richard III. When you run the SAXLocalNameCount on it, you should see the following output.

The SAXLocalNameCount program parses the XML file, and provides a count of the number of instances of each type of XML tag that it contains.

Open the file data/rich_iii.xml in a text editor.

To check that the error handling is working, delete the closing tag from an entry in the XML file, for example the closing tag

, from line 30, shown below.

EDWARD, Prince of Wales, afterwards King Edward V.


Run SAXLocalNameCount again.

This time, you should see the following fatal error message.

% java sax/SAXLocalNameCount data/rich_iii.xml Exception in thread «main» org.xml.sax.SAXException: Fatal Error: URI=file:install-dir /JAXP_sources/jaxp-1_4_2-release-date/samples/data/rich_iii.xml Line=30: The element type «PERSONA» must be terminated by the matching end-tag «

As you can see, when the error was encountered, the parser generated a SAXParseException, a subclass of SAXException that identifies the file and the location where the error occurred.

Java SAX Parsing

13 Haji [2011-12-22 12:42:00]

Есть поток XML, который мне нужно проанализировать. Поскольку мне нужно только сделать это один раз и построить мои объекты Java, SAX выглядит как естественный выбор. Я расширяю DefaultHandler и реализую методы startElement, endElement и characters, имея членов в моем классе, где я сохраняю текущее значение чтения (взятое в методе символов).

У меня нет проблем с тем, что мне нужно, но мой код стал довольно сложным, и я уверен, что нет причин для этого и что я могу делать что-то по-другому. Структура моего XML выглядит примерно так:

Моя проблема началась, когда я понял, что те же имена тегов используются в нескольких областях файла. Например, id и name существуют как для игрока, так и для команды. Я хочу создать экземпляры моих классов java Player и Team. Во время разбора я сохранил логические флаги, говорящие мне, есть ли я в разделе команд, чтобы в конце этого элемента я узнал, что имя — это имя команды, а не имя игрока и т.д.

Вот как выглядит мой код:

Поскольку в моем реальном сценарии у меня есть больше узлов для игрока в дополнение к командам, и эти узлы также имеют теги, такие как имя и идентификатор, я обнаружил, что меня перепутали с несколькими булерами, подобными inTeamsSection, и мой метод endElement становится длинным и комплекс со многими условиями.

Что мне делать по-другому? Как я могу узнать, что, например, тег имени принадлежит?

5 ответов

20 Решение Jörn Horstmann [2011-12-22 15:08:00]

При написании SAX-анализатора существует один аккуратный трюк: разрешено изменять ContentHandler XMLReader во время разбора. Это позволяет отделить логическая разборка для разных элементов в несколько классов, что делает синтаксический анализ более модульного и многоразового использования. Когда один обработчик видит свой конечный элемент, переключается обратно на родителя. Сколько обработчиков, которые вы реализуете, будет вы. Код будет выглядеть так:

Трудно советовать, не зная больше о ваших требованиях, но тот факт, что вы удивлены тем, что «мой код стал довольно сложным», говорит о том, что вы не были хорошо осведомлены, когда выбрали SAX. SAX — это низкоуровневый программный интерфейс, способный к очень высокой производительности, но это потому, что парсер делает гораздо меньше работы для вас, и поэтому вам нужно сделать гораздо больше работы самостоятельно.

Я настоятельно рекомендую прекратить парсинг самостоятельно и захватить хорошую библиотеку привязки XML-данных. XStream (http://x-stream.github.io/) может быть личным фаворитом, но там много разных библиотек. Он может быть даже способен анализировать ваши POJO на месте, без какой-либо конфигурации (если вы используете имена свойств и плюрализацию для соответствия структуре XML).

Я делаю что-то очень похожее, но вместо того, чтобы иметь флаги boolean , чтобы сообщить мне, в каком состоянии я вхожу, я тестирую player или team как null . Делает вещи немного аккуратнее. Это требует, чтобы вы установили их в null , когда вы обнаруживаете конец каждого элемента, после того как вы добавили его в соответствующий список.

Если вам нужен более красивый код, используйте StAX, это сравнение всех XML-синтаксических API говорит о том, что StAX — намного лучший вариант.

Производительность StAX в большинстве тестов лучше, чем в любой другой реализации API.

Поэтому я лично не вижу никаких оснований для продолжения работы SAX, если вы не выполняете некоторые связанные с устаревшим программированием.

Создание XML с использованием SAX и Java

Кто-нибудь знает хороший учебник (или имеет хороший пример) для написания XML с использованием среды SAX (или чего-то подобного) и Java? Поиск принес очень мало с точки зрения полезных результатов. Я пытаюсь экспортировать из приложения для Android, и я стараюсь избегать как можно большего объема памяти.

Там очень полезный метод для генерации XML непосредственно из POJO через фреймворк SAX (не SAX-анализатор, а SAX-framework). Этот метод может быть использован для создания XML-документа.

По существу, вы добавляете методы в свой POJO или записываете класс утилиты для POJO, которые превращают их в эмитентов событий SAX (при синтаксическом анализе XML-документа обычно возникают события, такие как SAX-парсер). Теперь ваш «генератор событий SAX» выглядит как выходная сторона синтаксического анализа SAX и может быть предоставлен любой обработчик содержимого, который будет принимать парсер SAX, например, тот, который pretyy печатает XML. Но он также может быть подан на парсер DOM для генерации дерева DOM или подачи в механизм XSLT для генерации HTML или выполнения истинного преобразования XSL без необходимости сначала генерировать промежуточный XML-документ из POJO.

Например, класс Person может иметь метод emitXML() , который включает следующие строки:

Update:

Несколько других ссылок:

Несколько ответов на комментарии:

Это верно, но описанный выше интерфейс XMLStreamWriter гораздо удобнее. — Майкл Кей 3 часа назад

Да, но я думаю, что я не был ясен. Я мог бы легко пройти иерархию и использовать XMLStreamWriter для непосредственного вывода XML-документа в поток. Тем не менее, в статьях показан мощный метод прохождения иерархии и генерации событий SAX, а не вывод XML-документа напрямую. Теперь я могу подключать разные обработчики контента, которые делают разные вещи или генерируют разные версии XML. Мы также могли бы кормить нашу иерархию объектов любым инструментом, который принял парсер SAX, например движок XSLT. Это действительно просто использование шаблона посетителя, установленного в рамках SAX: мы разделяем перемещение иерархии с вывода XML. Части, которые выводят XML, обработчики содержимого, обязательно должны использовать XMLStreamWriter , если их целью является запись XML-потока.

Например, в нашей программе мы отправили XML-сообщения через сетевые сокеты между распределенными компонентами, и мы также использовали XSLT для генерации наших HTML-страниц. Раньше мы проходили нашу иерархию для создания XML-документа (строки), а затем либо записывали этот XML-документ в сетевой сокет, либо передавали этот документ движку XSLT (который по существу просто разбирал его снова). После использования этого метода мы могли бы по существу перенести нашу иерархию объектов (используя этот SAX-адаптер) непосредственно на движок XSLT без необходимости использования промежуточной строки XML. Также было удобно использовать один обработчик контента для создания компактного представления XML для сетевого потока и использовать другой для создания довольно печатного документа XML для записи в файл журнала.

Кроме того, использование API-анализатора SAX для записи XML является неправильным использованием API, IMHO. — Puce 49 мин назад

Возможно, но я думаю, что это зависит от ваших потребностей. Если требование OP заключается только в том, чтобы выписать конкретный XML-документ, то это определенно излишне. Тем не менее, я думал, что стоит упомянуть, использует ли OP другой метод в своем проекте, о котором он не упоминал. Нет никакого вреда в подаче альтернативной идеи.

Вызов неправильного использования может быть немного сильным, но я согласен, что вы имеете право на свое мнение. Он документирован в учебнике Oracle, поэтому его не считают злоупотреблением инженерами Sun/Oracle. Это было очень успешным в нашем проекте, чтобы помочь нам удовлетворить наши требования без существенных минусов, поэтому я буду придерживаться такого подхода в своей панели инструментов, когда это будет полезно в будущем.

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих