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));
}