Опубликован: 22.12.2015 | Уровень: для всех | Доступ: платный
Лекция 27:

XML Парсинг

< Лекция 26 || Лекция 27 || Лекция 28 >

Запрос XML-данных

При разработке мобильных приложений, использующих обмен данными по протоколу HTTP используется XML-формат. XML — это формат документов для структурированных данных, он используется не только для коммуникации, но и для хранения данных. Его используют также для создания интерфейса и хранения ресурсов. В предлагаемом примере мы поговорим о парсинге XML-данных.

Создайте приложение 'XmlParseSample'. Откройте файл исходного кода и внесите в него следующие изменения.

#include "xmlparsesamle.h"

#include <libxml/HTMLparser.h>
typedef struct appdata {

Evas_Object *win;
Evas_Object *conform;
Evas_Object *label;
bool value_begin;
int value_type;
char buffer[1024];
} appdata_s;

libxml/HTMLparser.h — это заголовочный файл библиотеки парсинга XML.

value_begin — это переменная для хранения информации о том, были ли запущены данные.

value_type - это переменная для хранения типа данных.

Buffer[] - это массив строковых переменных для хранени декодированных данных.

Далее мы приведем простое выражение в формате XML. name характеризует название узла, Elsa характеризует данные. Проведем декодирование данных.

<name>Elsa</name><math>95</math>

Добавьте новую функцию выше create_base_gui(). Эта функция добавляет элементы управления в контейнер Box.

static void
my_box_pack(Evas_Object *box, Evas_Object *child, double h_weight, double v_weight, double h_align, double v_align)
{
/* create a frame we shall use as padding around the child widget */
Evas_Object *frame = elm_frame_add(box);
/* use the medium padding style. there is "pad_small", "pad_medium",
* "pad_large" and "pad_huge" available as styles in addition to the
* "default" frame style */
elm_object_style_set(frame, "pad_medium");
evas_object_show(frame);
}

Затем добавим код для создания метки и кнопки в функцию create_base_gui().

/* Conformant */
ad->conform = elm_conformant_add(ad->win);
elm_win_indicator_mode_set(ad->win, ELM_WIN_INDICATOR_SHOW);
elm_win_indicator_opacity_set(ad->win, ELM_WIN_INDICATOR_OPAQUE);
evas_object_size_hint_weight_set(ad->conform, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(ad->win, ad->conform);
evas_object_show(ad->conform);

{
Evas_Object *box = elm_box_add(ad->win);
evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_object_content_set(ad->conform, box);
evas_object_show(box);
{
/* Label*/
ad->label = elm_label_add(ad->conform);
elm_object_text_set(ad->label, "<align=center>XML Parsing</>");
//evas_object_size_hint_weight_set(ad->label, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
//elm_object_content_set(ad->conform, ad->label);
my_box_pack(box, ad->label, 1.0, 0.0, -1.0, 0.5);

/* Button-1 */
Evas_Object *btn = elm_button_add(ad->conform);
elm_object_text_set(btn, "This is a button");
evas_object_smart_callback_add(btn, "clicked", btn_parse1_cb, ad);
my_box_pack(box, btn, 1.0, 0.0, -1.0, 1.0);
/* Show window after base gui is set up */
evas_object_show(ad->win);
}

Добавим также две функции выше create_base_gui().

void
walkTree1(xmlNode * a_node, appdata_s *ad)
{
xmlNode *cur_node = NULL;
xmlChar *key = NULL;
xmlChar *value = NULL;
for (cur_node = a_node; cur_node; cur_node = cur_node->next)
{
if(!strcmp((const char*)cur_node->name, "name"))
ad->value_type = 1;
if(!strcmp((const char*)cur_node->name, "math"))
ad->value_type = 2;
if(!strcmp((const char*)cur_node->name, "text")) {
if( ad->value_type == 1 )
{
value = cur_node->content;
strcat(ad->buffer, "Name : ");
strcat(ad->buffer, (char*)value);
ad->value_type = 0;
}
else if( ad->value_type == 2 )
{
value = cur_node->content;
strcat(ad->buffer, " / Math : ");
strcat(ad->buffer, (char*)value);
strcat(ad->buffer, "<br/>");
ad->value_type = 0;
}
}
walkTree1(cur_node->children, ad);
}
}
static void
btn_parse1_cb(void *data, Evas_Object *obj, void *event_info)
{
appdata_s *ad = data;
const char* buf = "<name>Elsa</name><math>95</math>";
htmlParserCtxtPtr parser = htmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL, 0);
htmlCtxtUseOptions(parser, HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_NONET);
htmlParseChunk(parser, buf, strlen(buf), 0);
ad->buffer[0] = '\0';
ad->value_type = 0;
walkTree1(xmlDocGetRootElement(parser->myDoc), ad);
elm_object_text_set(ad->label, ad->buffer);
}

walkTree1() - это функция, которая выделяет данные, соответствующие полям "name" и "math" в объекте xmlNode и добавляет даные к глобальной переменной.

Запросите индивидуальные узлы, включенные в XML-выражение из цикла и, впоследствии если имя данного индивидуального узла будет 'name' или 'math,' запросите данные. Функция вызывает сама себя для того, чтобы перемещаться на следующий узел.

XmlNode — это переменная структуры, которая указывает на определенный указатель в выражении XML. Его свойства следующие:

  • next: возвращает указатель следующего узла.
  • name: имя узла. Если 'text' сохранен в этом свойстве, это означает, что узел является данными. В таких случаях данные могут быть запрошены из контентного свойства.
  • content: данные узла.
  • properties: данные свойств узла.

btn_parse1_cb() - это функция, которая создает объект XML parser, вводит выражение XML, и затем вызывает функцию, которая запускает парсинг.

htmlCreatePushParserCtxt(htmlSAXHandlerPtr sax, void *user_data, char *chunk, int size, char *filename, xmlCharEncoding enc) - это API, который создает объект XML parser.

HtmlParserCtxtPtr — это структура XML parser.

htmlCtxtUseOptions(htmlParserCtxtPtr ctxt, int options) — это API, который определяет опцию для пармера XML. Эта опция может быть указана с избыточностью

htmlParseChunk(htmlParserCtxtPtr ctxt, char *chunk, int size, int terminate) - это API, который вводит выражение XML в парсер XML.

xmlDocGetRootElement(xmlDocPtr doc) — это API, который возвращает начальный узел выражения XML.

Теперь мы можем запустить наш пример. Нажмите на кнопку и убедитесь в том, что данные отображаются на экране.


Запрос данных об узле

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

<student name="Aurora" math="27"></student>

Добавьте код, создающий вторую кнопку в конец функции create_base_gui().

/* Button-1 */
Evas_Object *btn = elm_button_add(ad->conform);
elm_object_text_set(btn, "This is a button");
evas_object_smart_callback_add(btn, "clicked", btn_parse1_cb, ad);
my_box_pack(box, btn, 1.0, 0.0, -1.0, 1.0);

/* Button-2 */
btn = elm_button_add(ad->conform);
elm_object_text_set(btn, "Parse2");
evas_object_smart_callback_add(btn, "clicked", btn_parse2_cb, ad);
my_box_pack(box, btn, 1.0, 0.0, -1.0, 1.0);
}

Добавьте две функции выше create_base_gui().

void
walkTree2(xmlDoc *doc, xmlNode * a_node, appdata_s *ad)
{
xmlNode *cur_node = NULL;
xmlAttr *cur_attr = NULL;
xmlChar *key = NULL;
for (cur_node = a_node; cur_node; cur_node = cur_node->next)
{
for (cur_attr = cur_node->properties; cur_attr; cur_attr = cur_attr->next) {
key = xmlGetProp(cur_node, cur_attr->name);
if(!strcmp((const char*)cur_attr->name, "name"))
{
strcat(ad->buffer, "Name : ");
strcat(ad->buffer, key);
}
if(!strcmp((const char*)cur_attr->name, "math"))
{
strcat(ad->buffer, " / Math : ");
strcat(ad->buffer, key);
strcat(ad->buffer, "<br/>");
}
if(key!=NULL) { xmlFree(key); key=NULL; }
}
walkTree2(doc, cur_node->children, ad);
}
}
static void
btn_parse2_cb(void *data, Evas_Object *obj, void *event_info)
{
appdata_s *ad = data;
const char* buf = "<?xml version=\"1.0\" encoding=\"utf-8\"?> <grade> <name>M.I.T</name> <student name=\"Aurora\" math=\"27\"></student> <studentname=\"Piana\" math=\"88\"></student> <student name=\"Tangled\" math=\"77\"></student> </grade>";
htmlParserCtxtPtr parser = htmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL, 0);
htmlCtxtUseOptions(parser, HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_NONET);
htmlParseChunk(parser, buf, strlen(buf), 0);
ad->buffer[0] = '\0';
walkTree2(parser->myDoc, xmlDocGetRootElement(parser->myDoc), ad);
elm_object_text_set(ad->label, ad->buffer);
}

walkTree2() - это функция, которая запрашивает данные о свойствах узла XML и отображает их на экране.

Запрос индивидуальных узлов включен в выражение XML и первого цикла for, а запрос индивидуальных свойств из второго цикла for. Далее, если имя заданного индивидуального узла будет 'name' или 'math', запрашиваются данные. Для перехода на следующий узел функция вызывает сама себя.

Среди свойств xmlNode, свойство 'properties' содержит свойства узла в форме xmlAttr.

xmlGetProp(xmlNodePtr node, const xmlChar *name) - это API, который возвращает данные о свойствах узла. Передайте узла XML в качестве перкого параметра и передайте имя свойства в качестве второго параметра.

xmlFree(xmlChar*) - это API, который удаляет данные XML.

btn_parse2_cb() - это функция, которая создает объект XML parser, вводит выражение XML и, затем, вызывает функцию, которая запускает парсинг данных свойств.

Запустите пример. Нажмите вторую кнопку. На экране будут показаны данные о свойствах.


Многоузловой парсинг

Следующее выражение XML имеет в общем три узла <student>, и каждый узел <student> включает в себя узел <name>и узел <math>. Мы поговорим о том, как получить доступ к узлам с помощью древовидной структуры

<student><name>Obama</name><math>50</math></student><student><name>Psy</name> <math>70</math></student><student><name>Yuna</name><math>65</math></student>.

Добавьте код третьей кнопки в конец функции create_base_gui().

/* Button-2 */
btn = elm_button_add(ad->conform);
elm_object_text_set(btn, "Parse2");
evas_object_smart_callback_add(btn, "clicked", btn_parse2_cb, ad);
my_box_pack(box, btn, 1.0, 0.0, -1.0, 1.0);

/* Button-3 */
btn = elm_button_add(ad->conform);
elm_object_text_set(btn, "Parse3");
evas_object_smart_callback_add(btn, "clicked", btn_parse3_cb, ad);
my_box_pack(box, btn, 1.0, 0.0, -1.0, 1.0);
}

Добавьте две функции выше create_base_gui().

void
walkTree3(xmlDoc *doc, xmlNode * a_node, appdata_s *ad)
{
xmlNode *cur_node = NULL;
xmlAttr *cur_attr = NULL;
xmlChar *key = NULL;
xmlChar *value = NULL;
for (cur_node = a_node; cur_node; cur_node = cur_node->next)
{
if(!strcmp((const char*)cur_node->name, "student") )
ad->value_begin = true;
if(!strcmp((const char*)cur_node->name, "name") && ad->value_begin)
ad->value_type = 1;
if(!strcmp((const char*)cur_node->name, "math") && ad->value_begin)
ad->value_type = 2;
if(!strcmp((const char*)cur_node->name, "text")) {
if( ad->value_type == 1 )
{
value = (char*)cur_node->content;
strcat(ad->buffer, "Name : ");
strcat(ad->buffer, (char*)value);
ad->value_type = 0;
}
else if( ad->value_type == 2 )
{
value = (char*)cur_node->content;
strcat(ad->buffer, " / Math : ");
strcat(ad->buffer, (char*)value);
strcat(ad->buffer, "<br/>");
ad->value_type = 0;
}
}
walkTree3(doc, cur_node->children, ad);
}
}
static void
btn_parse3_cb(void *data, Evas_Object *obj, void *event_info)
{
appdata_s *ad = data;
const char* buf = "<?xml version=\"1.0\" encoding=\"utf-8\"?> <grade> <name>M.I.T</name> <student><name>Obama</name><math>50</math></student>
<student><name>Psy</name><math>70</math></student> <student><name>Yuna</name><math>65</math></student> </grade>";
htmlParserCtxtPtr parser = htmlCreatePushParserCtxt(NULL, NULL, NULL, 0, NULL,0);
htmlCtxtUseOptions(parser, HTML_PARSE_NOBLANKS | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING | HTML_PARSE_NONET);
htmlParseChunk(parser, buf, strlen(buf), 0);
ad->value_begin = false;
ad->buffer[0] = '\0';
ad->value_type = 0;
walkTree3(parser->myDoc, xmlDocGetRootElement(parser->myDoc), ad);
elm_object_text_set(ad->label, ad->buffer);
}

walkTree3() - это функция, которая запрашивает данные второго узла в древовидной структуре XML и отображает ее на экране. Эта функция выделяет узлы из выражения XML один за другим. Если имя узла будет 'student,' то эта функция определяет узел как первый узел и присваивает глобальной переменной 'value_begin' значение true. Если имя узла будет 'name' или 'math,' а значение глобальной переменной 'value_begin' равно true, то функция определяет этот узел как вторичный и вызывает сама себя. Если имя узла будет 'text', то он определяется как данные и затем считывается значение его свойства content и добавляет значение к глобальной переменной. Для перехода на следующий узел функция вызывает сама себя.

btn_parse3_cb() - это функция, которая создает объект XML parser, водит выражение XML, и , затем, запускает парсинг данных второго узла.

Запустите пример и нажмите третью кнопку. На экране появятся данные второго узла.


Родственные API

htmlParserCtxtPtr htmlCreatePushParserCtxt(htmlSAXHandlerPtr sax, void *user_data, char *chunk, int size, char *filename, xmlCharEncoding enc): API, который создает объект XML parser .

htmlParserCtxtPtr: структура парсера XML. Среди свойств myDoc содержит выражение XML в форме xmlDocPtr.

int htmlCtxtUseOptions(htmlParserCtxtPtr ctxt, int options): API, который определяет опцию для парсера XML.Эта опция может быть избыточной.

int htmlParseChunk(htmlParserCtxtPtr ctxt, char *chunk, int size, int terminate): API, который вводит выражение XML в парсер XML.

xmlNodePtr xmlDocGetRootElement(xmlDocPtr doc): API, который возвращает начальный узел выражения XML .

xmlChar* xmlGetProp(xmlNodePtr node, const xmlChar *name): API, который возвращает данные свойств узла. Передайте узел XML первому параметру, и передайте имя свойства второму параметру.

void xmlFree(xmlChar*): - это API, который удаляет данные XML.

< Лекция 26 || Лекция 27 || Лекция 28 >