Spec-Zone .ru
спецификации, руководства, описания, API
След: API Java для XML, Обрабатывающего (JAXP)
Урок: Простой API для XML
Парсинг XML-файла Используя SAX
Домашняя страница > API Java для XML, Обрабатывающего (JAXP) > Простой API для XML

Парсинг XML-файла Используя SAX

В реальных применениях Вы будете хотеть использовать синтаксический анализатор SAX, чтобы обработать данные XML и сделать что-то полезное с этим. Этот раздел исследует пример программа JAXP, SAXLocalNameCount, который считает число элементов, используя только компонент localName элемента в XML-документе. Имена пространства имен игнорируются для простоты. Этот пример также показывает, как использовать SAX ErrorHandler.


Отметьте - После того, как Вы загрузили и установили источники API JAXP от области загрузки JAXP, пример программы для этого примера находится в каталоге install-dir/jaxp-1_4_2-release-date/samples/sax. XML-файлы, с которыми это взаимодействует, находятся в install-dir/jaxp-1_4_2-release-date/samples/data.


Создание Скелета

Программа SAXLocalNameCount создается в файле под названием SAXLocalNameCount.java.

public class SAXLocalNameCount {
    static public void main(String[] args) {
        // ...
    }
}

Поскольку Вы выполните это автономный, Вы нуждаетесь в методе main(). И Вы нуждаетесь в параметрах командной строки так, чтобы можно было сказать приложение который файл обработать.

Импорт Классов

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

package sax;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

import java.util.*;
import java.io.*;

public class SAXLocalNameCount {
    // ...
}

Пакет javax.xml.parsers содержит SAXParserFactory class, который создает используемый экземпляр синтаксического анализатора. Это бросает ParserConfigurationException, если это не может произвести синтаксический анализатор, который соответствует указанную конфигурацию опций. (Позже, Вы будете видеть больше о параметрах конфигурации). Пакет javax.xml.parsers также содержит SAXParser class, который является тем, что фабрика возвращает для того, чтобы проанализировать. Пакет org.xml.sax определяет все интерфейсы, используемые для синтаксического анализатора SAX. Пакет org.xml.sax.helpers содержит DefaultHandler, который определяет class, который обработает события SAX, которые генерирует синтаксический анализатор. Классы в java.util и java.io, необходимы, чтобы обеспечить хэш-таблицы и вывести.

Установка ввода-вывода

Первый порядок бизнеса должен обработать параметры командной строки, которые на данном этапе только служат, чтобы заставить имя файла обрабатывать. Следующий код в методе main говорит приложение, какой файл Вы хотите, чтобы SAXLocalNameCountMethod обработал.

static public void main(String[] args) throws Exception {
    String filename = null;

    for (int i = 0; i < args.length; i++) {
        filename = args[i];
        if (i != args.length - 1) {
            usage();
        }
    }

    if (filename == null) {
        usage();
    } 
}

Это кодовые наборы основной метод, чтобы бросить Exception, когда это встречается с проблемами, и определяет параметры командной строки, которые обязаны говорить приложению имя XML-файла, который будет обработан. Другие параметры командной строки в этой части кода будут исследованы позже в этом уроке, когда мы начнем смотреть на проверку допустимости.

Строка filename, которую Вы даете, когда Вы запускаете приложение, будет преобразована в java.io.File URL внутренним методом, convertToFileURL(). Это делается следующим кодом в SAXLocalNameCountMethod.

public class SAXLocalNameCount {
    private static String convertToFileURL(String filename) {
        String path = new File(filename).getAbsolutePath();
        if (File.separatorChar != '/') {
            path = path.replace(File.separatorChar, '/');
        }

        if (!path.startsWith("/")) {
            path = "/" + path;
        }
        return "file:" + path;
    }

    // ...
}

Если неправильные параметры командной строки определяются, когда программа выполняется, то метод usage() приложения SAXLocalNameCount вызывается, чтобы распечатать корректные экранные опции.

private static void usage() {
    System.err.println("Usage: SAXLocalNameCount <file.xml>");
    System.err.println("       -usage or -help = this message");
    System.exit(1);
}

Дальнейшие опции usage() будут исследованы позже в этом уроке, когда проверка допустимости будет адресоваться.

Реализация Интерфейса ContentHandler

Самым важным интерфейсом в SAXLocalNameCount является ContentHandler. Этот интерфейс требует многих методов, которые синтаксический анализатор SAX вызывает в ответ на различные события парсинга. Главные методы обработки событий: startDocument, endDocument, startElement, и endElement.

Самый легкий способ реализовать этот интерфейс состоит в том, чтобы расширить DefaultHandler class, определенный в пакете org.xml.sax.helpers. Тот class обеспечивает пустые методы для всех событий ContentHandler. Пример программы расширяет тот class.

public class SAXLocalNameCount extends DefaultHandler {
    // ...
} 

Отметьте - DefaultHandler также определяет пустые методы для других крупных событий, определенных в DTDHandler, EntityResolver, и интерфейсах ErrorHandler. Вы узнаете больше о тех методах позже в этом уроке.


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

Обработка Событий Контента

Этот раздел показывает код, который обрабатывает события ContentHandler.

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

События документа

Следующий код обрабатывает события запускать-документа и документа конца:

public class SAXLocalNameCount extends DefaultHandler {
    
    private Hashtable tags;

    public void startDocument() throws SAXException {
        tags = new Hashtable();
    }

    public void endDocument() throws SAXException {
        Enumeration e = tags.keys();
        while (e.hasMoreElements()) {
            String tag = (String)e.nextElement();
            int count = ((Integer)tags.get(tag)).intValue();
            System.out.println("Local Name \"" + tag + "\" occurs " 
                               + count + " times");
        }    
    }
 
    private static String convertToFileURL(String filename) {
        // ...
    }

    // ...
}

Этот код определяет то, что делает приложение, когда синтаксический анализатор встречается с запуском и конечными точками проанализированного документа. Метод startDocument() интерфейса ContentHandler создает экземпляр java.util.Hashtable, который в Событиях Элемента будет заполнен с элементами XML, которые синтаксический анализатор находит в документе. Когда синтаксический анализатор достигает конца документа, метод endDocument() вызывается, чтобы получить имена и количества элементов, содержавшихся в хэш-таблице, и распечатать сообщение, экранное, чтобы сказать пользователю, сколько падений каждого элемента было найдено.

Оба из этих методов ContentHandler бросают SAXException s. Вы узнаете больше об исключениях SAX в Установке Обработки ошибок.

События элемента

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

public void startDocument() throws SAXException {
    tags = new Hashtable();
}

public void startElement(String namespaceURI,
                         String localName,
                         String qName, 
                         Attributes atts)
    throws SAXException {

    String key = localName;
    Object value = tags.get(key);

    if (value == null) {
        tags.put(key, new Integer(1));
    } 
    else {
        int count = ((Integer)value).intValue();
        count++;
        tags.put(key, new Integer(count));
    }
}
 
public void endDocument() throws SAXException {
    // ...
}

Этот код обрабатывает теги элемента, включая любые атрибуты, определенные в теге запуска, чтобы получить пространство имен универсальный идентификатор ресурса (URI), локальное имя и полностью определенное имя того элемента. Метод startElement() тогда заполняет карту хеша, создаваемую startDocument() с локальными именами и количествами этого для каждого типа элемента. Отметьте что, когда метод startElement() вызывается, если обработка пространства имен не включается, то локальное имя для элементов и атрибутов, могло оказаться, было пустой строкой. Код обрабатывает тот случай при использовании полностью определенного имени всякий раз, когда простое имя является пустой строкой.

Символьные События

API SAX JAXP также позволяет Вам обрабатывать символы, которые синтаксический анализатор поставляет Вашему приложению, используя метод ContentHandler.characters().


Отметьте - события Character не демонстрируются в примере SAXLocalNameCount, но краткое описание включается в этот раздел для законченности.


Синтаксические анализаторы не обязаны возвращать любое определенное число символов когда-то. Синтаксический анализатор может возвратить что-либо из единственного символа за один раз до нескольких тысяч и все еще быть приспосабливающей стандарту реализацией. Так, если Ваше приложение должно обработать символы, оно видит, мудро иметь метод characters(), накапливают символы в java.lang.StringBuffer и работают на них только, когда Вы уверены, что все они были найдены.

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

<para>This paragraph contains <bold>important</bold> ideas.</para>

Начальный текст, This paragraph contains, завершается запуском элемента <bold>. Текст important завершается конечным тэгом, </bold>, и заключительным текстом, ideas., завершается конечным тэгом, </para>.

Чтобы быть строго точным, символьный обработчик должен отсканировать для символов амперсанда (&) и символов левой угловой скобки (<) и заменить их строками &amp; или &lt;, как соответствующих. Это объясняется в следующем разделе.

Обработка Специальных Символов

В XML объект является структурой XML (или простой текст), у которого есть имя. Ссылка на объект по имени заставляет это быть вставленным в документ вместо ссылки на сущность. Чтобы создать ссылку на сущность, Вы окружаете имя объекта амперсандом и точкой с запятой:

&entityName;

Когда Вы обрабатываете большие блоки XML или HTML, которые включают много специальных символов, можно использовать раздел CDATA. Раздел CDATA работает как <code>...</code> в HTML, только больше так: весь пробел в разделе CDATA является существенным, и символы в нем не интерпретируются как XML. Раздел CDATA запускается с <![[CDATA[ и заканчивается ]]>.

Пример раздела CDATA, взятого от демонстрационного XML-файла install-dir/jaxp-1_4_2-release-date/samples/data/REC-xml-19980210.xml, показывают ниже.

<p><termdef id="dt-cdsection" term="CDATA Section"<<term>CDATA sections</term> 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 "<code>&lt;![CDATA[</code>" and end with the string "<code>]]&gt;</code>"

После того, как проанализированный, этот текст был бы выведен на экран следующим образом:

Разделы CDATA могут произойти где угодно, символьные данные могут произойти; они используются, чтобы выйти из блоков текста, содержащего символы, которые были бы иначе распознаны как разметка. Разделы CDATA начинаются со строки "<![CDATA[" и заканчивают строкой "]]>".

Существование CDATA делает надлежащее повторение XML немного хитрым. Если текст, который будет выведен, не находится в разделе CDATA, то любые угловые скобки, амперсанды, и другие специальные символы в тексте должны быть заменены соответствующей ссылкой на сущность. (Заменяющий левые угловые скобки и амперсанды является самым важным, другие символы будут интерпретироваться должным образом, не вводя в заблуждение синтаксический анализатор.), Но если синтезируемый текст находится в разделе CDATA, то замены не должны произойти, приводя к тексту как этот в более раннем примере. В простой программе, такой как наше приложение SAXLocalNameCount, это не особенно серьезно. Но много фильтрующих XML приложений будут хотеть отследить то, появляется ли текст в разделе CDATA, так, чтобы они могли обработать специальные символы должным образом.

Установка Синтаксического анализатора

Следующие кодовые наборы синтаксический анализатор и запустили это:

static public void main(String[] args) throws Exception {

    // Code to parse command-line arguments 
    //(shown above)
    // ...

    SAXParserFactory spf = SAXParserFactory.newInstance();
    spf.setNamespaceAware(true);
    SAXParser saxParser = spf.newSAXParser();
}

Эти строки кода создают экземпляр SAXParserFactory, как определено установкой системного свойства javax.xml.parsers.SAXParserFactory. Фабрика, которая будет создана, устанавливается, чтобы поддерживать пространства имен XML установкой setNamespaceAware к истине, и затем экземпляр SAXParser получается из фабрики, вызывая ее метод newSAXParser().


Отметьте - javax.xml.parsers.SAXParser, class является оберткой, которая определяет много методов удобства. Это обертывает (несколько менее дружественный) объект org.xml.sax.Parser. Если нужно можно получить тот синтаксический анализатор, используя метод getParser() SAXParser class.


Вы теперь должны реализовать XMLReader, который должны реализовать все синтаксические анализаторы. XMLReader используется приложением, чтобы сказать синтаксический анализатор SAX, какую обработку это должно выполнить на рассматриваемом документе. XMLReader реализуется следующим кодом в методе main.

// ...
SAXParser saxParser = spf.newSAXParser();
XMLReader xmlReader = saxParser.getXMLReader();
xmlReader.setContentHandler(new SAXLocalNameCount());
xmlReader.parse(convertToFileURL(filename));

Здесь, Вы получаете экземпляр XMLReader для своего синтаксического анализатора, вызывая Ваш метод getXMLReader() экземпляра SAXParser. XMLReader тогда регистрирует SAXLocalNameCount class как его обработчик содержимого, так, чтобы действия, выполняемые синтаксическим анализатором, были таковыми из startDocument(), startElement(), и методов endDocument(), показанных в Обработке Событий Контента. Наконец, XMLReader говорит синтаксический анализатор который документ проанализировать, передавая это расположение рассматриваемого XML-файла в форме File URL, сгенерированный методом convertToFileURL(), определенным в Установке ввода-вывода.

Установка Обработки ошибок

Вы могли начать использовать свой синтаксический анализатор теперь, но более безопасно реализовать некоторую обработку ошибок. Синтаксический анализатор может генерировать три вида ошибок: фатальная ошибка, ошибка, и предупреждение. Когда фатальная ошибка происходит, синтаксический анализатор не может продолжаться. Так, если приложение не генерирует исключение, то ошибочный обработчик событий значения по умолчанию генерирует тот. Но для нефатальных ошибок и предупреждений, исключения никогда не сгенерированы обработчиком ошибок значения по умолчанию, и никакие сообщения не выводятся на экран.

Как показано в Событиях Документа, методы обработки событий приложения бросают SAXException. Например, подпись метода startDocument() в интерфейсе ContentHandler определяется как возврат SAXException.

public void startDocument() throws SAXException { /* ... */ }

SAXException может быть создан, используя сообщение, другое исключение, или обоих.

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

xmlReader.setErrorHandler(new MyErrorHandler(System.err));

// ...

private static class MyErrorHandler implements ErrorHandler {
    private PrintStream out;

    MyErrorHandler(PrintStream out) {
        this.out = out;
    }

    private String getParseExceptionInfo(SAXParseException spe) {
        String systemId = spe.getSystemId();

        if (systemId == null) {
            systemId = "null";
        }

        String info = "URI=" + systemId + " Line=" 
            + spe.getLineNumber() + ": " + spe.getMessage();

        return info;
    }

    public void warning(SAXParseException spe) throws SAXException {
        out.println("Warning: " + getParseExceptionInfo(spe));
    }
        
    public void error(SAXParseException spe) throws SAXException {
        String message = "Error: " + getParseExceptionInfo(spe);
        throw new SAXException(message);
    }

    public void fatalError(SAXParseException spe) throws SAXException {
        String message = "Fatal Error: " + getParseExceptionInfo(spe);
        throw new SAXException(message);
    }
}

Таким же образом как в Установке Синтаксического анализатора, который показал XMLReader, указываемый на корректный обработчик содержимого, здесь на XMLReader указывают на новый обработчик ошибок, вызывая его метод setErrorHandler().

MyErrorHandler class реализует стандартный интерфейс org.xml.sax.ErrorHandler, и определяет метод, чтобы получить информацию об исключении, которая обеспечивается любыми экземплярами SAXParseException, сгенерированными синтаксическим анализатором. Этот метод, getParseExceptionInfo(), просто получает номер строки, в котором ошибка происходит в XML-документе и идентификаторе системы, на которой это работает, вызывая стандартные методы SAXParseException getLineNumber() и getSystemId(). Эта информация об исключении тогда питается в реализации основных методов error() обработки ошибок SAX, warning(), и fatalError(), которые обновляются, чтобы отправить соответствующие сообщения о природе и расположении ошибок в документе.

Обработка Ошибок NonFatal

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

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

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


Отметьте - Это может быть поучительно, чтобы исследовать методы обработки ошибок, определенные в org.xml.sax.helpers.DefaultHandler. Вы будете видеть, что методы error() И warning() ничего не делают, тогда как fatalError() выдает исключение. Конечно, Вы могли всегда переопределять метод fatalError(), чтобы выдать различное исключение. Но если Ваш код не выдаст исключение, когда фатальная ошибка произойдет, тогда то синтаксический анализатор SAX будет. Спецификация XML требует этого.


Обработка Предупреждений

Предупреждения, также, игнорируются по умолчанию. Предупреждения информативны и могут только быть сгенерированы в присутствии DTD или схемы. Например, если элемент определяется дважды в DTD, предупреждение сгенерировано. Это не недопустимо, и это не вызывает проблемы, но это - что-то, что Вам могло бы понравиться знать о том, потому что это, возможно, не было намеренно. Проверку допустимости XML-документа против DTD покажут в разделе.

Выполнение Примера Синтаксического анализатора SAX без Проверки допустимости

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

Следующие шаги объясняют, как выполнить пример синтаксического анализатора SAX без проверки допустимости.

Выполнять Пример SAXLocalNameCount без Проверки допустимости

  1. Переместитесь к каталогу samples.% cd install-dir/jaxp-1_4_2-release-date/samples.
  2. Скомпилируйте пример class.% javac sax/*
  3. Выполните программу SAXLocalNameCount на XML-файле.

    Выберите один из XML-файлов в каталоге data и выполните программу SAXLocalNameCount на этом. Здесь, мы хотели выполнять программу на файле rich_iii.xml.

    % java sax/SAXLocalNameCount data/rich_iii.xml

    XML-файл rich_iii.xml содержит версию XML пьесы Шекспира Уильяма Ричард III. Когда Вы выполняете SAXLocalNameCount на этом, следует видеть следующий вывод.

    Local Name "STAGEDIR" occurs 230 times
    Local Name "PERSONA" occurs 39 times
    Local Name "SPEECH" occurs 1089 times
    Local Name "SCENE" occurs 25 times
    Local Name "ACT" occurs 5 times
    Local Name "PGROUP" occurs 4 times
    Local Name "PLAY" occurs 1 times
    Local Name "PLAYSUBT" occurs 1 times
    Local Name "FM" occurs 1 times
    Local Name "SPEAKER" occurs 1091 times
    Local Name "TITLE" occurs 32 times
    Local Name "GRPDESCR" occurs 4 times
    Local Name "P" occurs 4 times
    Local Name "SCNDESCR" occurs 1 times
    Local Name "PERSONAE" occurs 1 times
    Local Name "LINE" occurs 3696 times
    

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

  4. Откройте файл data/rich_iii.xml в текстовом редакторе.

    Чтобы проверить, что обработка ошибок работает, удалите закрывающий тэг из записи в XML-файле, например закрывающий тэг </PERSONA>, от строки 30, показанный ниже.

    30 <PERSONA>EDWARD, Prince of Wales, afterwards King Edward V.</PERSONA>
  5. Выполните SAXLocalNameCount снова.

    На сей раз следует видеть следующее сообщение о фатальной ошибке.

    % 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 "</PERSONA>".

    Как можно видеть, когда с ошибкой встретились, синтаксический анализатор, сгенерированный SAXParseException, подкласс SAXException, который идентифицирует файл и расположение, где ошибка произошла.


Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Когда Использовать SAX
Следующая страница: Реализация Проверки допустимости SAX