Поиск по этому блогу

вторник, 30 августа 2011 г.

SAX парсер на Java своими руками.

Привет %username%.

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

Рассмотрим пример простейшего SAX парсера.



Пусть у нас есть XML документ:

<?xml version="1.0" encoding="windows-1251"?>
<books>
    <book id="1">
        <title>Компьютерные сети</title>
        <autor>Олифер</autor>
    </book>
    <book id="2">
        <title>Вычислительные сети сети</title>
        <autor>Кто-то ещё</autor>
    </book>
</books>


Код обработки документа может выглядеть следующим образом:

package javaxmltest;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;

// SAX
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 *
 * @author nicolay
 */
public class Main {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        SAXParserFactory factory = SAXParserFactory.newInstance();

        factory.setValidating(true);
        factory.setNamespaceAware(false);
        SAXParser parser;

        InputStream xmlData = null;
        try
        {
            xmlData = new FileInputStream("books.xml");

            parser = factory.newSAXParser();
            parser.parse(xmlData, new MyParser());

            
        } catch (FileNotFoundException e)
        {
            e.printStackTrace();
            // обработки ошибки, файл не найден
        } catch (ParserConfigurationException e)
        {
            e.printStackTrace();
            // обработка ошибки Parser
        } catch (SAXException e)
        {
            e.printStackTrace();
            // обработка ошибки SAX
        } catch (IOException e)
        {
            e.printStackTrace();
            // обработка ошибок ввода
        } 

    }
}
// http://java.sun.com/j2se/1.4.2/docs/api/org/xml/sax/helpers/DefaultHandler.html
class MyParser extends DefaultHandler {

    @Override
    public void startElement(String uri, String localName, String qName,
            Attributes attributes) throws SAXException {
        System.out.println("Тег: "+qName);
        if(qName.equals("book"))
            System.out.println("id книги "+attributes.getValue("id"));
  //      System.out.println(attributes.getLength());
        super.startElement(uri, localName, qName, attributes);

        
    }

    @Override
    public void characters(char[] c, int start, int length) 
                                                 throws SAXException {
        super.characters(c, start,  length);
        for(int i=start;i< start+length;++i)
            System.err.print(c[i]);
    }
  
    @Override
    public void endElement(String uri, String localName, String qName) 
                                                    throws SAXException {
        
        System.out.println("Тег разобран: "+qName);
        super.endElement(uri,localName, qName);
    }

    @Override
    public void startDocument() throws SAXException {
        System.out.println("Начало разбора документа!");
        super.startDocument();
    }

    @Override
    public void endDocument() throws SAXException {
        super.endDocument();
        System.out.println("Разбор документа окончен!");

    }
    
}
Код можно получить тут: https://github.com/png-tech/PngJavaSamples/blob/master/SimpleExamples/src/png/samples/xml/saxread/XmlSaxReadSample.java

Поясняю код. Мы описываем класс  MyParser, который будет обрабатывать события чтения данных. Выделено 5 событий:

  1. старт документ
  2. открытие тега
  3. данные внутри тега
  4. закрытие тега
  5. заканчиваем обработку документа 

SAX парсер постепенно читает файл XML. И когда набирается достаточное количество данных (считался тег с атрибутами) вызывает метод нашего класса. Когда данные уже отработаны, они в принципе не нужны и поэтому постепенно удаляются из памяти.

Такая методика позволяет обрабатывать достаточно большие XML(возможно размером несколько мегабайт/гигабайт). Это главное достоинство SAX парсеров. Скорость и возможность обрабатывать большие объемы данных.

Главный недостаток -  сложный код в случае сложной структуре XML файла. То есть если XML простой и линейный, то его легко анализировать SAX-парсером. Для XML со сложной структурой придется по возиться с алгоритмизацией.

Вернемся к примеру.
1-ое событие - начали обрабатывать документ. Наступает лишь раз в момент, когда мы начали анализировать файл. На этом этапе можно производить первичную инициализацию данных
2-ое событие - открылся тег. На этом этапе мы знаем имя тега и его атрибуты. Мы не знаем ни как глубоко вложен элемент, ни сколько там ещё внутри. Если это надо, то нужно обрабатывать алгоритмически
3-е событие  - данные. Реальные данные между открытым и закрытым тегами. Нам дают массив символов, делайте с ним что хотите
4-ое событие - закрываем тег. Теперь мы знаем, тег закрыт, можем что-нибудь обработать алгоритмически
5-ое событие - документ обработан. Освобождаем лишние ресурсы, делаем пост обработку, если она нужна. Короче радуемся жизни, т.к. мы такие молодцы - обработали документ.

В итоге программа выведет :

Начало разбора документа!
Тег: books

Тег: book
id книги 1
    
Тег: title
Тег разобран: title
        Компьютерные сети
Тег: autor
Тег разобран: autor
        Олифер
Тег разобран: book
    
Тег: book
id книги 2
    
Тег: title
Тег разобран: title
        Вычислительные сети сети
Тег: autor
Тег разобран: autor
        Кто-то ещё
Тег разобран: book
    

Тег разобран: books
Разбор документа окончен!

Надеюсь пример кому-нибудь помог.
Спасибо за внимание.


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

Исходники примера можно взять тут:
https://github.com/png-tech/PngJavaSamples/blob/master/SimpleExamples/src/png/samples/xml/saxread/


11 комментариев:

  1. Большое спасибо за простое и понятное описание.

    ОтветитьУдалить
  2. Этот комментарий был удален автором.

    ОтветитьУдалить
  3. The best ! Спасибо. Понял на вашем примере.

    ОтветитьУдалить
  4. Начало разбора документа!
    Тег: books
    Тег: book
    id книги 1
    Тег: title
    Тег разобран: title

    Тег: autor

    Тег разобран: autor
    Компьютерные сети
    Тег разобран: book
    Олифер
    Тег: book

    id книги 2

    Тег: title
    Вычислительные сети сети
    Тег разобран: title
    Кто-то ещё
    Тег: autor

    Тег разобран: autor
    Тег разобран: book
    Тег разобран: books
    Разбор документа окончен!

    У меня он так парсит. ничего понять не могу. Куда смотреть подскажите?

    ОтветитьУдалить
    Ответы
    1. Информации для анализа маловато, но рискну предположить, что проблема в кодировке файла, который вы анализируете. То есть символы не верной кодировки не выводятся на консоль, или выводятся пробелами, или как-то ещё. Предлагаю воспользоваться отладчиком(debuger) для обнаружения проблемы. В методы класса MyParser поставить точки останова(break point) и посмотреть кто и сколько раз эти методы вызывает и с какими параметрами.

      Удалить
  5. Спасибо большое, только у вас всё понял

    ОтветитьУдалить
  6. у меня из-за factory.setValidating(true) выдавало ошибку "no validating saxparser implementation available".

    убрал - и все заработало. возможно тут прикол в том, что пишу под андроида, не знаю.

    ОтветитьУдалить
    Ответы
    1. возможно :) под андройдом писать не приходилось :)

      Удалить