Осваиваем SAX парсер: Quick start
Работая с JAXB и XPath API я столкнулся с необходимостью манипуляции namespace-ами XML-документов. Проблема решена и описана в посте Чтение XML-документа с помощью JAXB с заменой namespace. Решение этой проблемы было найдено в интернете. Ещё, небольшие поиски по интернету показали, что в большинстве случаев, когда речь идёт о SAX API, везде приводят один и тот же пример замены namespace-ов. Я решил разобраться с ситуацией и отправился на официальный сайт http://www.saxproject.org/. На официальном сайте я перечитал документацию и нашёл, почему везде выложен один и тот же пример. Этот же пример присутствует и в официальной документации…
Тем не менее, SAX API весьма важен и лежит в основе многих других технологий, поэтому я решил изучить SAX API немного подробнее. Ниже перевод материалов с официального сайта. Я нашёл их весьма интересными и заслуживающими более подробного изучения.
Оглавление
Требования
SAX — общий интерфейс реализованный для многими XML парсерами (и штуками, позиционирующими себя как XML парсеры), он практически является на столько же общим API для работы с XML, насколько JDBC является общим интерфейсом, реализованным для различных реляционных баз данных (и штруковинами, позиционирующими себя как реляционные базы данных). Если вы хотите использовать SAX, вам потребуются всё перечисленное ниже:
- Java 1.1 или современнее.
- SAX2-совместимый XML парсер, указанный в вашем Java classpath. Если вам нужен подобный парсер, смотрите ссылки.
- SAX2 дистрибутив, указанный в вашем Java classpath. (Наиболее вероятно, вы получите его вместе с вашим парсером).
- Большинство Java/XML дистрибутивов включают SAX2 парсер. Большинство Web application серверов используют его для работы с XML. Все дистрибутивы с поддержкой JAXP 1.1 включают поддержку SAX2.
Парсинг XML документа
Начните созданием класса, который наследует (extends) DefaultHandler:
Так как это Java — приложение, мы создадим статический метод main, который использует метод createXMLReader класса XMLReaderFactory для выбора SAX драйвера динамически (программно). Обработка исключений удалена из примера для облегчения понимания. Реальное приложение должно обрабатывать исключения:
В случае, если ваша Java среда разработки автоматически не добавляет compiled-in default (или не настраивает META-INF/services/org.xl.sax.driver system resource), вам вероятно потребуется установить в Java system property оrg.xml.sax.driver значение, указывающее полный путь к SAX driver, как в примере:
Множество разных SAX2 драйверов распространены на сегодняшний день Поэтому в качестве имени класса вы можете использовать, например:
| Class Name | Notes |
|---|---|
| gnu.xml.aelfred2.SAXDriver | Lightweight non-validating parser; Free Software |
| gnu.xml.aelfred2.XmlReader | Optionally validates; Free Software |
| oracle.xml.parser.v2.SAXParser | Optionally validates; proprietary |
| org.apache.crimson.parser.XMLReaderImpl | Optionally validates; used in JDK 1.4; Open Source |
| org.apache.xerces.parsers.SAXParser | Optionally validates; Open Source |
Иначе, если вас не беспокоит привязка вашего приложения к определённому SAX драйверу, вы можете использовать его конструктор напрямую. Мы предполагаем, что SAX driver для вашего XML парсера называется com.example.xml.SAXDriver, однако, в реальности такого класса не существует. Вы должны знать имя настоящего драйвера для вашего парсера, чтобы использовать такой подход.
Созданный объект может быть использован для парсинга XML-документов, но сначала, нужно зарегистрировать обработчики событий, которые парсер может использовать для передачи информации. Это делается с помощью методов setContentHandler и setErrorHandler интерфейса XMLReader. В настоящем приложении, эти обработчики будут обычно разными объектами, но для приведённого простого примера, мы помещаем эти обработчики в Top-level class. Таким образом нам нужно создать наш класс и зарегистрировать его в XMLReader:
Приведённый пример создаёт экземпляр MySAXApp для получения XML parsing events и регистрирует его в XML reader для получения regular content событий и error событий (есть другие виды событий, но они редко используются). Теперь давайте предположим, что все аргументы командной строки — имена файлов и мы будем пытаться парсить их по-одному используя метод parse из интерфейса XMLReader:
Заметьте, что каждый reader должен быть представлен InputSource объектом, чтобы быть «распарсенным». Ниже полный пример описанного demo класса:
Вы можете откомпилировать этот код и запустить его (Не забудьте указать SAX driver класс в свойстве org.xml.sax.driver), однако ничего особенного не возникнет, если только ваш документ не содержит «плохой» XML, поскольку вы пока не установили обработчиков SAX событий для вашего приложения.
Обработка событий
Всё становится интересней когда вы начинаете реализовывать методы для реакции на XML parsing events ( помните мы зарегистрировали наш класс для получения XML parsing events в предыдущем разделе). Наиболее важные события — начало и завершение документа (start and end of the document), начало и завершение элемента (start and end of elements) и символьных данных.
Чтобы узнать про начало и завершение документа, клиентское приложение реализует методы startDocument и endDocument.
Обработчики событий start/endDocument не имеют аргументов. Когда SAX driver находит начало документа, он вызовет функцию startDocument один раз, когда найдёт завершение документа — вызовет endDocuemnt (даже если были обнаружены ошибки).
Приведённые примеры печатаю сообщения в стандартный output, но ваше приложение может содержать любой код для этих обработчиков, наиболее часто, этот код будет создавать какой то тип дерева в памяти, делать вывод, производить сохранение в базу данных или извлекать информацию из XML stream.
SAX driver будет сообщать о стартовых и конечных элементах практически также за исключением того, что он будет передавать несколько параметров в методы startElement и endElement.
Приведенные методы будут печатать в начале и конце каждого элемента. Перед именем элемента (local name) будет печататься namespace URI в фигурных скобках. Параметр qName содержит имя без namespace в формате XML 1.0, которое вы должны использовать для всех элементов не имеющих namespace URI. В этом Quick Introduction, мы не будем рассматривать как обрабатываются атрибуты. Атрибуты могут быть получены по имени или итерацией по ним как будто они хранятся в массиве.
Наконец, SAX2 передает (reports) обычные символьные данные через метод characters. Следующий пример напечатает все символьные данные на экран. Output будет возможно слегка длиннее потому что он производит escaping специальных символов (pretty-prints the output by escaping special characters):
Заметьте, что SAX driver может разбивать символьные данные как угодно, так что вы не можете рассчитывать на то, что все символьные данные придут в одном событии (single characters event).
Пример SAX2 приложения
Ниже пример готового приложения (конечно в настоящем приложении обработчики событий будут скорее всего реализованы в отдельном классе, а не в классе приложения).
Sample Output
Предположим обрабатывается следующий XML документ:
Если этот документ назван roses.xml и у вас есть SAX2 driver в classpath с именем com.example.xml.SAXDriver (приведённый в примере на деле не существует), в можете вызвать пример приложения примерно так:
При запуске, вы получить output подобный этому:
Заметьте, что даже этот короткий документ генерирует (по крайней мере) 25 событий: один на старт и завершение каждого из шести элементов (тегов), один на каждый из одиннадцати фрагментов символьных данных и один на старт и завершение документа.
Если у входного документа нет namespace нет атрибута xmlns=»http://www.megginson.com/ns/exp/poetry» указывающего, что элементы находятся в указанном namespace, outoput будет выглядеть подобно этому:
Наиболее вероятно, вам придется работать с обеими типами документов: c документами использующими XML namespace-ы и не использующими их. Документы могут быть также с разными элементами (часть элементов и атрибутов с namespace, часть нет). В любом случае убедитесь, что ваш код проверяет namespace URI элементов, а не просто рассчитывает на то, что он всегда есть или его всегда нет.
Разработка ПО и многое другое
Сегодняшней темой будет парсинг XML. Существуют две стратегии обработки XML документов: SAX и DOM.
SAX парсеры предлагают потоковую обработку данных основанную на событиях.
DOM парсеры преобразуют XML в дерево объектов, с которыми можно будет работать.
Рассмотрим пример простейшего SAX парсера.
Пусть у нас есть XML документ:
Код обработки документа может выглядеть следующим образом:
Поясняю код. Мы описываем класс MyParser , который будет обрабатывать события чтения данных. Выделено 5 событий:
- старт документ
- открытие тега
- данные внутри тега
- закрытие тега
- заканчиваем обработку документа
Такая методика позволяет обрабатывать достаточно большие XML(возможно размером несколько мегабайт/гигабайт). Это главное достоинство SAX парсеров. Скорость и возможность обрабатывать большие объемы данных.
Главный недостаток — сложный код в случае сложной структуре XML файла. То есть если XML простой и линейный, то его легко анализировать SAX-парсером. Для XML со сложной структурой придется по возиться с алгоритмизацией.
Вернемся к примеру.
1-ое событие — начали обрабатывать документ. Наступает лишь раз в момент, когда мы начали анализировать файл. На этом этапе можно производить первичную инициализацию данных
2-ое событие — открылся тег. На этом этапе мы знаем имя тега и его атрибуты. Мы не знаем ни как глубоко вложен элемент, ни сколько там ещё внутри. Если это надо, то нужно обрабатывать алгоритмически
3-е событие — данные. Реальные данные между открытым и закрытым тегами. Нам дают массив символов, делайте с ним что хотите
4-ое событие — закрываем тег. Теперь мы знаем, тег закрыт, можем что-нибудь обработать алгоритмически
5-ое событие — документ обработан. Освобождаем лишние ресурсы, делаем пост обработку, если она нужна. Короче радуемся жизни, т.к. мы такие молодцы — обработали документ.
В итоге программа выведет :
Надеюсь пример кому-нибудь помог.
Спасибо за внимание.
UPD: Сделал репозиторий на github, где буду выкладывать примеры для статей.
Советы по написанию парсера файлов на Java? [закрытый]
интерпретация блоков CSV является основным вопросом здесь.
Я знаю, как прочитать файл во что-то вроде String[] и некоторые из основных особенностей String , но я не думаю, что используя такие методы, как contains() и анализируя все символ за символом будет работать.
каким образом я могу сделать это в разумный способ?
-barfoob: boobs, foob, «foo bar»
12 ответов
есть причина, по которой все предполагают, что вы говорите о XML: изобретение проприетарного текстового формата файла требует очень сильная обоснование в лице зрелостью и доступностью XML-парсеров.
и ваш вопрос указывает на то, что у вас очень мало предварительных знаний о парсерах (в противном случае вы бы написали ANTLR или JavaCC грамматика вместо того, чтобы задавать этот вопрос) — что является еще одним веским аргументом против сворачивания собственного, кроме как в качестве учебного опыта.
Так как вход » отформатирован аналогично HTML«, то, вероятно, ваши данные лучше всего представлены с использованием древовидной структуры, а также, вероятно, что это XML или аналогично XML.
Если это так, я предлагаю самый умный способ проанализировать ваш файл-использовать синтаксический анализатор XML.
вот некоторые ресурсы, которые вы можете найти полезными:
- глава о синтаксическом анализе XML от Sun: http://java.sun.com/developer/Books/xmljava/ch03.pdf
- статья, которая может помочь вам начать работу qucikly:http://onjava.com/pub/a/onjava/2002/06/26/xml.html
Если документ является допустимым XML, то любой из других ответов будет работать. Если это не так, вы будете должны lex.
вы должны посмотреть ANTLR, даже если вы хотите написать парсер самостоятельно, ANTLR является отличной альтернативой. Или хотя бы посмотрите на YAML
этой и копаться в Википедии похожие статьи будет вполне достаточно.
в зависимости от того, насколько сложна ваша «схема», регулярное выражение может быть то, что вы хотите. Если вложенности много, то проще всего преобразовать в XML или JSON и использовать встроенный синтаксический анализатор.
люди правы в том, что стандартные форматы являются лучшей практикой, но давайте отложим это в сторону.
предполагая, что приведенный вами пример является репрезентативным, задача довольно тривиальна.
вы показываете строку с начальным маркером, отмеченную двоеточием, а затем список значений, разделенных запятыми. Разделите сначала двоеточие-пробел, а затем используйте split() на части справа. Обработка котировок также тривиальна.
после просмотра вашего образца ввода, я не вижу никакого сходства с HTML или XML:
-barfoob: boobs, foob, «foo bar»
если это то, что вы хотите разобрать, у меня есть альтернативное предложение, использовать синтаксический анализатор свойств Java (поставляется со стандартной Java), а затем разобрать оставшуюся часть каждой строки, используя свой собственный код. Вам нужно будет несколько изменить свой формат, чтобы это работало, так что это зависит от вас.
barfoob=boobs, foob, «foo bar»
свойства Java будут возможность вернуть вас barfoob как имя свойства и boobs, foob, «foo bar» как значение свойства. Вот где вы можете использовать свой пользовательский код, чтобы разделить значение свойства на boobs , foob и foo bar .
Я настоятельно рекомендую не изобретать колесо и использовать существующее решение, такое как подчеркивание, Fixedformat4j или jFFP это может все анализировать позиционные или разделенные запятыми файлы значений (лично я рекомендую Flatworm).
вы можете использовать Neko HTML parser в какой-то степени. Это зависит от того, как он обрабатывает нестандартный HTML.