object库仅仅提供了对指定对象元素解析模式,使用上较为简单方便,但是有所局限。 如果要支持大数据的xml解析,以及对元素的更灵活控制,可以直接使用tbox底层单独提供的xml模块。
tbox的xml库提供了两种解析模式:DOM解析和SAX解析。
DOM方式采用dom对象树,一次性解析到内存,这跟object的类似,但是可以控制所有元素标记。 SAX方式采用外部迭代模式,灵活性和性能更高。并且支持自定路径解析操作,类似xpath,可以选择指定路径,进行解析。
DOM模式比较简单,只要看下如下例子就能一目了然:
- // 初始化流
- tb_stream_ref_t istream = tb_stream_init_from_url("http://localhost/file.xml");
- if (istream)
- {
- // 打开流
- if (tb_stream_open(istream))
- {
- // 初始化读取器
- tb_xml_reader_ref_t reader = tb_xml_reader_init(istream);
- if (reader)
- {
- // 加载数据, root 为根节点
- tb_xml_node_t* root = tb_xml_reader_load(reader);
- if (root)
- {
- // 解析节点操作
- // ...
- // 释放根节点
- tb_xml_node_exit(root);
- }
- // 释放读取器
- tb_xml_reader_exit(reader);
- }
- }
- // 释放流
- tb_stream_exit(istream);
- }
SAX模式更加的高效、灵活,并且对大数据xml更好的做了支持,因为它是采用迭代器模式, 一边读一边解,只对自己感兴趣的数据进行解析,更加的节省内存,不需要所有都加载到内存中。 因此配合stream,可以实现对网络数据流式解析。
具体就不多说了,直接上代码吧:
- // 初始化流
- tb_stream_ref_t istream = tb_stream_init_from_url("http://localhost/file.xml");
- if (istream)
- {
- // 打开流
- if (tb_stream_open(istream))
- {
- // 初始化读取器
- tb_xml_reader_ref_t reader = tb_xml_reader_init(istream);
- if (reader)
- {
- // 初始化xml读取器事件
- tb_size_t event = TB_XML_READER_EVENT_NONE;
- // 遍历所有xml节点元素, 如果返回空事件, 则结束
- while ((event = tb_xml_reader_next(reader)))
- {
- switch (event)
- {
- // xml文档节点类型事件
- case TB_XML_READER_EVENT_DOCUMENT:
- {
- tb_printf("<?xml version = \"%s\" encoding = \"%s\" ?>\n"
- , tb_xml_reader_version(reader), tb_xml_reader_charset(reader));
- }
- break;
- // 文档类型节点类型事件
- case TB_XML_READER_EVENT_DOCUMENT_TYPE:
- {
- tb_printf("<!DOCTYPE>\n");
- }
- break;
- // 空元素节点类型事件,例如: <element/>
- case TB_XML_READER_EVENT_ELEMENT_EMPTY:
- {
- // 节点元素名
- tb_char_t const* name = tb_xml_reader_element(reader);
- // 节点元素属性列表
- tb_xml_node_t const* attr = tb_xml_reader_attributes(reader);
- // xml节点层次,用于显示缩进排版
- tb_size_t t = tb_xml_reader_level(reader);
- while (t--) tb_printf("\t");
- // 遍历所有元素属性
- if (!attr) tb_printf("<%s/>\n", name);
- else
- {
- tb_printf("<%s", name);
- for (; attr; attr = attr->next)
- tb_printf(" %s = \"%s\"", tb_pstring_cstr(&attr->name), tb_pstring_cstr(&attr->data));
- tb_printf("/>\n");
- }
- }
- break;
- // 元素开始节点事件,例如: <element> ...
- case TB_XML_READER_EVENT_ELEMENT_BEG:
- {
- // 节点元素名
- tb_char_t const* name = tb_xml_reader_element(reader);
- // 节点元素属性列表
- tb_xml_node_t const* attr = tb_xml_reader_attributes(reader);
- // xml节点层次,用于显示缩进排版
- tb_size_t t = tb_xml_reader_level(reader) - 1;
- while (t--) tb_printf("\t");
- // 遍历所有元素属性
- if (!attr) tb_printf("<%s>\n", name);
- else
- {
- tb_printf("<%s", name);
- for (; attr; attr = attr->next)
- tb_printf(" %s = \"%s\"", tb_pstring_cstr(&attr->name), tb_pstring_cstr(&attr->data));
- tb_printf(">\n");
- }
- }
- break;
- // 元素结束节点事件,例如:.. </element>
- case TB_XML_READER_EVENT_ELEMENT_END:
- {
- tb_size_t t = tb_xml_reader_level(reader);
- while (t--) tb_printf("\t");
- tb_printf("</%s>\n", tb_xml_reader_element(reader));
- }
- break;
- // 文本节点事件
- case TB_XML_READER_EVENT_TEXT:
- {
- tb_size_t t = tb_xml_reader_level(reader);
- while (t--) tb_printf("\t");
- tb_printf("%s", tb_xml_reader_text(reader));
- tb_printf("\n");
- }
- break;
- // CDATA节点事件, 例如: <!CDATA[data]>
- case TB_XML_READER_EVENT_CDATA:
- {
- tb_size_t t = tb_xml_reader_level(reader);
- while (t--) tb_printf("\t");
- tb_printf("<![CDATA[%s]]>", tb_xml_reader_cdata(reader));
- tb_printf("\n");
- }
- break;
- // 注释节点事件,例如: <!-- comment -->
- case TB_XML_READER_EVENT_COMMENT:
- {
- tb_size_t t = tb_xml_reader_level(reader);
- while (t--) tb_printf("\t");
- tb_printf("<!--%s-->", tb_xml_reader_comment(reader));
- tb_printf("\n");
- }
- break;
- default:
- break;
- }
- }
- // 释放读取器
- tb_xml_reader_exit(reader);
- }
- }
- // 释放流
- tb_stream_exit(istream);
- }
如果想针对性进行解析,可以通过tb_xml_reader_goto定位到指定的path路径开始解析:
// 初始化流
tb_stream_ref_t istream = tb_stream_init_from_url("http://localhost/file.xml");
if (istream)
{
// 打开流
if (tb_stream_open(istream))
{
// 初始化读取器
tb_xml_reader_ref_t reader = tb_xml_reader_init(istream);
if (reader)
{
// 将reader跳转到指定路径
if (tb_xml_reader_goto(reader, "/root/node/data"))
{
// 加载数据, root 为根节点
tb_xml_node_t* root = tb_xml_reader_load(reader);
if (root)
{
// 解析节点操作
// ...
// 释放根节点
tb_xml_node_exit(root);
}
}
// 释放读取器
tb_xml_reader_exit(reader);
}
}
// 释放流
tb_stream_exit(istream);
}
其中的tb_xml_node_t节点类型,其实就是一个树形链表,如果你一次性加载了整个对象树, 也是可以很方便的对其进行遍历的:
// 节点类型定义描述,其他的所有节点都是继承此节点
typedef struct __tb_xml_node_t
{
/// 节点的类型
tb_size_t type;
/// 节点的名字
tb_pstring_t name;
/// 节点的数据
tb_pstring_t data;
/// 下个节点,单链表
struct __tb_xml_node_t* next;
// 子节点的头部,单链表
struct __tb_xml_node_t* chead;
// 子节点的尾部
struct __tb_xml_node_t* ctail;
// 子节点的数量
tb_size_t csize;
// 属性节点的头部,单链表
struct __tb_xml_node_t* ahead;
// 属性节点的尾部
struct __tb_xml_node_t* atail;
// 属性节点的数量
tb_size_t asize;
/// 父节点
struct __tb_xml_node_t* parent;
}tb_xml_node_t;
遍历所有子节点:
tb_xml_node_t* head = node->chead;
for (node = head; node; node = node->next)
{
// 这里只处理元素节点:<element>...</element> 或者 <element/>
if (node->type == TB_XML_NODE_TYPE_ELEMENT)
{
// 元素节点的名字大小
tb_size_t m = tb_pstring_size(&node->name);
// 打印元素节点名子
tb_trace_d("%s", tb_pstring_cstr(&node->name));
}
}
遍历所有属性节点:
tb_xml_node_t* head = node->ahead;
for (node = head; node; node = node->next)
{
// 打印属性节点的名字和数据,例如: attr_name="data"
tb_trace_d("%s=\"%s\"", tb_pstring_cstr(&node->name), tb_pstring_cstr(&node->data));
}