Библиотека сайта rus-linux.net
Возможности тулкита GTK+ и сопутствующих библиотек
Автор: А.Панин
Дата публикации: 3 декабря 2014 г.
Простой механизм разбора документов формата XML
- При генерации документа формата XML может использоваться исключительно кодировка UTF-8
- В генерируемом документе не могут использоваться объявленные пользователем сущности (entities)
- Описание типа документа, инструкции обработки документа и комментарии могут использоваться при генерации документа, но они не будут обрабатываться каким-либо образом, а будут просто "пропускаться"
- При разборе документа не будет осуществляться проверка его корректности, поэтому объявление типа документа может не использоваться
- Элементы
- Атрибуты
- Стандартные сущности:
&
,&alt;
,>
,"
,'
- Ссылки на символы
- Секции, отмеченные, как CDATA
Следует учитывать тот факт, что описываемый механизм может обрабатывать некоторые документы, которые не принимаются полнофункциональными системами для разбора документов формата XML, однако, в случае нарушения форматирования документа он будет прекращать его разбор и сообщать об ошибке.
1. Разбор и сохранение документов
В первую очередь следует обратить внимание на тот факт, что при работе с документами формата XML в случае использования библиотеки GLib не будет предоставляться каких-либо функций для автоматического формирования текстового представления результирующего файла (как это было в случае с объектом типа GKeyFile
, предоставляющим возможность работы с файлами, содержащими пары ключ-значение). Исходя из этого, для сохранения данных конфигурации приложения в форме документа формата XML в коде приложения придется объявлять переменные, в которых будут храниться значения параметров конфигурации приложения. Кроме того, придется самостоятельно формировать текстовое представление документа формата XML и сохранять его в файл на диске. Перед разбором документа следует загрузить данные из соответствующего файла в память, так как механизм разбора документов формата XML из состава библиотеки GLib принимает исключительно адрес строкового представления файла, расположенного в памяти. При осуществлении загрузки содержимого файла в память может использоваться либо упомянутая в предыдущей статье функция g_file_get_contents()
для загрузки содержимого файла в резервируемый фрагмент памяти, либо функция g_mapped_file_new()
совместно с функцией g_mapped_file_get_contents()
для отображения файла в память. После этого следует объявить структуру типа GMarkupParser
.
Данная структура содержит указатели на функции обратного вызова, которые будут задействованы в ходе разбора документа формата XML. Если для разбора документа не требуется реализации каких-либо функций обратного вызова, в качестве значения указателя должно использоваться значение NULL. В любом случае для корректного разбора документа формата XML придется реализовать как минимум две функции обратного вызова, поэтому упомянутые функции заслуживают внимания:
-
Функция начала разбора элемента
void start_element(GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error);
Данная функция вызывается тогда, когда осуществляется разбор открывающихся тэгов документа (например,
<foo bar="baz">
), поэтому в рамках нее обычно устанавливается идентификатор элемента, который может быть определен на основе имени элемента, передаваемого с помощью аргументаelement_name
(в случае работы с рассматриваемым примером тэга с помощью аргументаelement_name
будет передана строка"foo"
). Также с помощью данной функции передаются имена и значения атрибутов, которые хранятся в массивах строкattribute_names
иattribute_values
соответственно (при работе с рассматриваемым примером тэга массивattribute_names
будет содержать значения"bar"
иNULL
, а массивattribute_values
- значения"baz"
иNULL
). С помощью аргументаuser_data
в функцию передаются пользовательские данные. Аргументerror
может быть задействован для передачи указания на ошибку из данной функции в функцию сообщения об ошибке, причем в качестве идентификаторов ошибок могут использоваться идентификаторыG_MARKUP_ERROR_UNKNOWN_ELEMENT
(для указания на неизвестный элемент),G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE
(для указания на неизвестный атрибут) иG_MARKUP_ERROR_INVALID_CONTENT
(для указания на некорректные данные). Первым же аргументом является указатель на объект контекста разбора документа типаGMarkupContext
, средствами которого осуществляется разбор документа. Реализация данной функции необходима для корректного разбора документа формата XML. -
Функция окончания разбора элемента
void end_element(GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error);
Данная функция вызывается тогда, когда осуществляется разбор закрывающихся тэгов документа (например,
</foo>
), поэтому в рамках нее может сбрасываться идентификатор разбираемого элемента. Кроме того, данная функция может вызываться при разборе пустых тегов, например,<foo/>
. Как и в предыдущей функции, с помощью аргументаelement_name
в функцию передается имя элемента (при работе с рассматриваемым примером тэга с помощью аргументаelement_name
будет передана строка"foo"
). Все остальные аргументы используются для передачи тех же данных, что и в предыдущей функции. -
Функция разбора текста
void text(GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error);
Данная функция вызывается каждый раз при разборе текста, расположенного между открывающимися и закрывающимися тэгами. Указатель на адрес начала фрагмента текста передается с помощью аргумента
text
, при этом сам текст не копируется в автоматически резервируемый фрагмент памяти, а также не завершается нулевым символом. По этим причинам с помощью аргументаtext_len
передается значение длины извлеченного фрагмента текста. Забегая вперед, следует отметить, что при разборе текста не происходит обратного преобразования стандартных сущностей, поэтому данное преобразование должно выполняться на уровне приложения. Реализация данной функции необходима для корректного разбора документа формата XML. -
Функция пропуска текста
void passthrough(GMarkupParseContext *context, const gchar *passthrough_text, gsize text_len, gpointer user_data, GError **error);
Данная функция работает аналогично функции разбора текста, но вызывается только тогда, когда текст должен быть сохранен без изменений и не может быть интерпретирован каким-либо образом. В частности, данная функция вызывается при разборе объявления типа документа, инструкций обработки документа и комментариев.
-
Функция обработки сообщения об ошибке
void error(GMarkupParseContext *context, GError *error, gpointer user_data);
Данная функция вызывается в случае обнаружения ошибки при разборе документа формата XML, причем ошибка может выявляться в рамках одной из описанных выше функций. Память, использованная для хранения данных ошибки не должна освобождаться вручную ни при каких обстоятельствах.
GMarkupParser
и присвоения значений адресов функций обратного вызова соответствующим указателям из этой структуры может быть создан объект контекста разбора документа XML типа GMarkupParseContext
. Следует помнить о том, что для разбора некоторых документов может быть задействовано несколько наборов функций обратного вызова, но обычно используется единственный набор. Объект контекста разбора документа формата XML создается с помощью функции g_markup_parse_context_new()
:
GMarkupParseContext * g_markup_parse_context_new(const GMarkupParser *parser, GMarkupParseFlags flags, gpointer user_data, GDestroyNotify user_data_dnotify);
В качестве первого аргумента функции передается указатель на объявленную ранее структуру типа GMarkupParser
с указателями на реализации функций обратного вызова, используемые для непосредственного доступа к данным документа. С помощью второго аргумента может быть передана бытовая маска из флагов операции разбора документа. Наиболее важными флагами операции разбора документа являются флаги G_MARKUP_TREAT_CDATA_AS_TEXT
(при использовании данного флага данные из секций CDATA будут передаваться в функцию разбора текста вместо функции пропуска текста) и G_MARKUP_PREFIX_ERROR_POSITION
(при использовании данного флага в случае самостоятельной передачи данных ошибок из первых четырех функций обратного вызова к описаниям ошибок будет автоматически добавляться информация о текущей позиции в документе точно так же, как это делается в случае обнаружения ошибки форматирования документа). С помощью аргумента user_data
в описанные функции обратного вызова передаются произвольные пользовательские данные, а с помощью аргумента user_data_dnotify
- указатель на функцию, которая будет вызвана перед уничтожением объекта контекста разбора документа формата XML и может использоваться для освобождения памяти, зарезервированной для хранения пользовательских данных.
g_markup_parse_context_parse()
:
gboolean g_markup_parse_context_parse(GMarkupParseContext *context, const gchar *text, gssize text_len, GError **error);
С помощью первого аргумента context
функции передается указатель на объект контекста разбора документа формата XML. С помощью аргументов text
и text_len
передаются указатель на строковое представление документа, расположенное в памяти, и длина этого строкового представления соответственно. При этом совсем не обязательно сразу же передавать полное строковое представление документа. Вместо этого вы можете повторно вызывать данную функцию, передавая новые фрагменты документа, что полезно, к примеру, при передаче данных документа по сети. Для строкового представления документа в любом случае должна использоваться кодировка UTF-8. В случае несоблюдения этого условия выполнение функции завершится неудачей, а с помощью аргумента error
будет возвращена информация об ошибке. После возникновения ошибки контекст разбора документа формата XML не может использоваться и должен быть уничтожен.
g_markup_parse_context_push()
и g_markup_parse_context_pop()
. Функция g_markup_parse_context_push()
может вызываться из функции начала разбора элемента при разборе определенного открывающегося тэга и позволяет задействовать для разбора документа измененный набор функций обратного вызова, сформированный в рамках структуры типа GMarkupParser
, причем эта функция должна использоваться совместно с функцией g_markup_parse_context_pop()
, которая должна вызываться из функции окончания разбора документа в момент разбора соответствующего закрывающегося тэга за исключением тех случаев, когда разбор документа прекращается ввиду ошибки:
void g_markup_parse_context_push(GMarkupParseContext *context, const GMarkupParser *parser, gpointer user_data); gpointer g_markup_parse_context_pop(GMarkupParseContext *context);
g_markup_parse_context_get_position()
может использоваться в функциях обработки сообщений об ошибках для получения текущей позиции в документе:
void g_markup_parse_context_get_position(GMarkupParseContext *context, gint *line_number, gint *char_number);
line_number
и char_number
возвращаются текущие номера строк и символов соответственно. Функция g_markup_parse_context_get_element()
может использоваться для получения имени открытого в текущий момент элемента в функциях начала и окончания разбора элемента:
const gchar *g_markup_parse_context_get_element(GMarkupParseContext *context);
g_markup_parse_context_get_element_stack()
может использоваться а рамках функций начала и окончания разбора элементов для получения связанного списка, содержащего имена элементов, причем первым именем будет имя открытого в текущий момент тэга, а вторым - имя родительского тэга:
const GSList *g_markup_parse_context_get_element_stack(GMarkupParseContext *context);
g_markup_parse_context_get_user_data()
может использоваться для получения пользовательских данных, переданных при вызове функции g_markup_parse_context_new()
или при последнем вызове функции g_markup_parse_context_push()
:
gpointer g_markup_parse_context_get_user_data(GMarkupParseContext *context);
g_markup_parse_context_end_parse()
используется для сообщения объекту контекста разбора документа формата XML о том, что работа с документом заканчивается. В том случае, если документ был передан не в полном объеме, выполнение данной функции завершится неудачей с возвратом данных об ошибке посредством аргумента error
:
gboolean g_markup_parse_context_end_parse(GMarkupParseContext *context, GError **error);
g_markup_parse_context_ref()
и g_markup_parse_context_unref()
соответственно:
GMarkupParseContext *g_markup_parse_context_ref(GMarkupParseContext *context); void g_markup_parse_context_unref(GMarkupParseContext *context);
g_markup_parse_context_free()
:
void g_markup_parse_context_free(GMarkupParseContext *context);
Следует помнить о том, что при разборе документов формата XML в функции обратного вызова будут передаваться исключительно фрагменты строкового представления этого документа, поэтому при необходимости извлечения значений определенных типов придется прибегнуть к вспомогательным функциям (таким, как функция atoi()
для получения целочисленных значений), а при извлечении строковых значений придется самостоятельно осуществлять замену стандартных сущностей на соответствующие им символы (вариант реализации функции, выполняющей данную операцию, содержится в примере ниже).
g_markup_escape_text()
, g_markup_printf_escaped()
и g_markup_vprintf_escaped()
. Функция g_markup_escape_text()
просто осуществляет замену специальных символов на стандартные сущности, а также замену управляющих символов за исключением символов табуляции, переноса строки и возврата каретки на соответствующие ссылки из диапазона &1; ... &1f; в переданной с помощью аргумента text
строке длиной в length
байт (в качестве значения длины строки может использоваться значение -1 при условии использования завершающего нулевого символа):
gchar *g_markup_escape_text(const gchar *text, gssize length);
Возвращаемая строка сохраняется в специально зарезервированном фрагменте памяти, который впоследствии должен быть освобожден. Функции g_markup_printf_escaped()
и g_markup_vprintf_escaped()
предназначены для выполнения аналогичной задачи, но отличаются от рассмотренной функции тем, что принимают строку форматирования с аргументами в стиле функции printf()
и различное число аргументов соответственно. Сохранение результирующего текстового представления на диск может осуществляться с помощью упомянутой в предыдущей статье функции g_file_set_contents()
.
Продолжение статьи : 2. Пример использования.