Простой XML сканер
XML reader - написанный в стиле минимализма. Предствляет собой небольшой конечный автомат,
на вход которого поступает XML текст, по символам. Доступно только чтение текстов, тегов, атрибутов и игнорирование коментариев.
Теги вида <? игнорируются (хотя их добавить будет легко). Всякие мало, нужные в простых ситуациях вещи,
навроде пространства имён, извратов в которых учавствуют квадратные скобки ([) не предусмотренны.
Чтение текста производится в мягком режиме, любые ошибки по возможности игнорируются. Например, если
попадаются атрибуты без значений, или значение атрибутов стоит без кавычек, то подобные атрибуты
тупо игнорируются. Пространство имён тегов, считывается как часть имени тега.
Сама возможность читать XML по символам, была необходима. Сам сканер изначально расчитан на работу в сети,
в результате чего возникаются два момента:
- Если применяется что-то похожее на Jabber протокол и с ним надо работать, то строить в памяти готового
дерева нельзя. Потому как Jabber не набор отдельных XML-лек летающих по сети. И их нельзя хватать и парсить
отдельно. Это потоковый протокол, в котором границы пакетов не определены. Вся Jabber сессия представляет
собой один единый xml файл. Первый тег которого открывается в первом пакете, и закрывается в момент
выхода с сервера. Мы можете в этом легко убедится, если попробуете зайти на любой Jabber сервер, набрав
первые пакеты по символам.
- Второй критерий. Расход памяти и производительность. Фокус в том, что при разборе пакетов,
нам нужны далеко не все теги или значения которые приходят по сети. Следовательно в идеале не
нужно много и тупо создавать копии каждого отрезка текста, во всякого рода буферах навроде std::string и т.п.
malloc функция сама по себе не очень быстрая, и память жрёт не рационально.
- Исходя из двух предыдущих пунктов, в принципе можно построить построить событийный сканер.
(это только в планах). Т.е. сделать некий чёрный ящик который общается с XML потоком, но не
требует чтобы общались непосредственно с самим XML. Попросту желательно иметь некий объект,
которому надо прописать геометрию требуемых структур или ветвей в XML, а так же те их части
данные, которых нас реально интересуют. А так же указать функции которые вызваются
для передачи содержимого отдельных обнаруженных структур XML дерева. По правде не очень пока понятно как это сделать.
Но затея вполне адекватная, мне доводилось сочинять что-то похожее в лиспе.
Опустимся на землю
В исходнике представленно три класса. Честно скажу - не тестировал. Просто прошёлся по паре XML файлов, вроде бы всё работает.
- XML_core - простой автомат, выделяющий из текста лексические еденицы. Выдаёт по отдельности названия тегов,
каждый атрибут тега, текст. Игнорирует коментарии и всё лишнее.
- XML_simple - производная от XML_core. Некое подобие синтаксичекой наслойки.
Содержит в себе стек и проверяет чтобы названия открывающих и закрывающих тегов совпадали.
Не совпадающие теги просто не закрывает. Так же смотрит тем чтобы небыло атрибутов в закрытых тегах, и прочие мелочи.
Атрибуты надо проверять, потому что они способны появлятся за пределами тега в некоторых ошибочных ситуациях.
Например </tag name='value'> - в этом случае атрибут прописан внутри закрывающего тега.
<tag/ name='value'> - в этом случае после появления '/' происходит закрытие текущего тега,
но его чтение самого тега при этом не прекращается. Чтобы не плодить в автомате лишних состояний,
проверка вынесена наружу.
- XML_tree - производная от XML_core. Строит в памяти дерево.
XML_core
Начальная функциональность.
- void ParseStart() - начальный запуск сканера (для первого запуска не нужен)
- bool ParseEnd() - остановка парсера. В приципе тоже не очень нужна. Если XML
файл правильный и кончается тегом, то никаких дополнительных действий по завершению не требуется.
Возврашает true если XML файл в данных момент не завершён.
- void Parse(char sym) - парсит очередной символ входного потока
- void Parse(char *text) - парсит несколько очередных символов потока
Перегружаемые функции обработки событий разбора:
- virtual void _open(const char*) - открытие тега
- virtual void _close(const char*) - закрытие тега. Даётся название без '/'. Если закрывается
текущий тег, то даётся пустое имя или 0 (частично это зависит от реализации классов строк.
- virtual void _text(const char*) - текст между тегами
- virtual void _attr(const char*,const char*) - атбирут тега. Даётся название и значение.
XML_simple
Минимальная проверка и стек.
- bool ParseEnd() - остановка парсера. Возврашает true если XML файл в данных момент не завершён.
Или стек не пустой.
Как говорилось выше, закрытие не совпадающих тегов не выполняется (беру пример с других).
В принципе можно сделать восстановление стека. Принудительное закрытие тега, если совпадающая пара,
есть недалеко от вершины стека.
XML_tree
Постройка дерева в памяти. Для хранения применяются вложенные STL списки.
Текстовые отрывки храняться как теги с незаданными родителями.
Download
xml.cpp