Skip to content

xml的解析

waruqi edited this page Sep 9, 2014 · 3 revisions

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));
}
Clone this wiki locally