• XML - XML学习/XML文件解析器(C++)实现


    XML - XML学习/XML文件解析器(C++)实现

    XML概述

    ​ XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这些部件加以标识。它也是元标记语言,用于定义其他与特定领域有关的,语义的,结构化的标记语言的句法语言。XML与HTML一样,都来自Standard Generalized Markup Language(标准通用标记语言,SGML)

    ​ SGML是一种用标记来描述文档资料的通用语言,它包含一系列的文档类型定义(Document Type Definition,DTD),DTD中定义了标记的含义,因而SGML的语法是可以拓展的。SGML十分庞大,不容易学习,也不容易使用,在计算机上实现也十分困难。

    ​ HTML只实现了SGML中很少的一部分标记,它的标记是固定的,语法也是不可拓展的,但是由于它语法的固定使得它简单易学,随着Web技术的发展而推广到全世界,但是由于HTML过于简单,且没有固定的格式,在发展的过程中遇到了一些问题,此时XML便应运而生。

    XML的特点
    1. 简介有效
      • XML是一个精简的SGML,它将SGML的丰富功能与HTML的易用性结合到Web应用中,保留了SGML的可拓展功能,这使得XML从根本上区别于HTML
    2. 易学易用
    3. 开放的国际化标准
      • XML标准。W3C正式批准的标准,这意味着这个标准是稳定的,完全可用于Web和工具的开发
      • XML名域标准。
      • DOM(Document Object Model,文档对象模型)标准。为给结构化的数据编写脚本提供标准,这样,开发人员就能够与计算机在基于XML的数据上进行交互
      • XSL标准。XSL有两个模块:XSL转换语言和XSL格式化对象。
      • XLL标准和XML指针语言(XPointer)标准。
    4. 高效且可扩充
      • XML支持复用文档片断,使用者可以发明和使用自己的标签,也可与他人共享,可延伸性大。

    总结:XML使用一个简单而又灵活的标准格式,为基于Web的应用提供一个描述数据和交换数据的有效手段。HTML描述了显示全球数据的通用方法,而XML提供了直接处理全球数据的通用方法。

    XML的作用

    ​ XML让信息的提供者可以根据需要,自行定义标记及属性名,结构化地描述信息内容

    1. 使得搜索更加有意义
    2. 开发灵活的Web应用软件
      • XML的应用使得三层Web应用软件的开发更加简单,由于其能够有效地进行消息和数据标准的统一,从而设计,开发出更加灵活的Web应用软件
    3. 实现不同数据的集成
    4. 使用于多种应用环境
    5. 客户端数据处理与计算
    6. 数据显示多样化
    7. 局部数据更新
    8. 与现有Web发布机制相兼容
    9. 可升级性
    10. 压缩性能高
      • XML压缩性能很好,因为用于描述数据结构的标签可以重复使用。
    XML的应用
    1. 应用于客户需要与不同的数据源进行交互时
    2. 应用于将大量运算负荷分布在客户端
    3. 应用于将同一数据以不同的面貌展现给不同的用户
    4. 应用于网络代理对所取得的信息进行编辑,增减以适应个人用户的需要

    解析XML

    ​ 一个实用的XML文档必须满足两点,分别是 组织良好(Well-formed)有效(Valid)

    XML文档

    一个组织良好的XML文档,需要满足以下三项基本规则:

    1. 文档以XML定义 开始
    2. 有一个包含所有其他内容的根元素
    3. 所有元素必须合理地嵌套,不允许交叉嵌套

    实例XML文件

    
    <students>
    	<student>
        	<id>202021091138id>
            <name>wzhname>
        student>
    students>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    DOM解析

    ​ 文档对象模型为XML文档的解析版本定义了一组接口。解析器读入整个文档,然后构建一个驻留内存的树结构,然后代码就可以使用DOM接口来操作这个树结构。

    ​ DOM提供了一组丰富的功能,用户可以使用这些功能来解释和操作XML文档,但是DOM解释XML文档也会遇到一些挑战和问题:

    1. DOM构建整个文档驻留内存的树。如果文档很大时,就会要求有极大的内存,这消耗的内存往往是XML文档的10~100倍左右
    2. DOM创建表示原始文档中每个东西的对象,包括元素,文本,属性和空格。如果用户只关注原始文档的一小部分,那么创建那些永远不被使用的对象是极其浪费的
    3. DOM解析器必须在代码取得控制权之前读取整个文档。对于非常大的文档,这会引起显著的延迟

    总结:除去以上的问题,DOM API是解析XML文档非常有用的方法

    SAX解析

    ​ SAX接口的出现是为了解决DOM解析XML文件面临的一些问题,SAX接口有如下特征:

    1. SAX解析器向代码发送事件。当解析器发现元素开始,元素结束,文本,文档的开始或结束时,它会告诉用户。
    2. SAX解析器根本不创建任何对象,它只是将事件传递给应用程序。如果希望基于哪些事件创建对象,这将由编程者自己来完成
    3. SAX解析器在解析开始的时候就开始发送事件。当解析器发现文档开始,元素开始和文本时,代码会收到一个事件。应用程序可以立即开始生成结果;不必一直等到整个文档被解析完毕。

    ​ 当然,SAX解析器也有一些不足点:

    1. SAX事件是无状态的。当SAX解析器在XML文档中发现文本时,它就向代码发送一个事件。该事件仅给用户发现的文本,它不告诉用户什么元素包含那个文本。如果想了解这一点,则用户必须自己编写状态管理代码
    2. SAX事件不是持久的。如果应用程序需要一个数据结构来对XML文档建模,则必须自己编写那样的代码。如果需要从SAX事件访问数据,并且没有把那个数据存储在代码中,那么用户不得不再次解析该文档

    以上是解析XML文档主要使用的2种方法,DOM解析器在解析XML文档时,会把文档中的所有元素,按照其出现的层次关系,解析成一个个Node对象(节点),可以遍历和修改节点,SAX解析器逐行扫描文档,一遍扫描一遍解析。相比于DOM,SAX可以在解析文档的任意时刻停止解析解析,是一种速度更快,更高效的方法。

    JDOM解析

    ​ JDOM是基于Java技术的开发源码项目,它试图遵循80/20原则:用DOM和SAX的20%的功能来满足80%的用户需求。JDOM的底层还是使用SAX和DOM解析器

    ​ JDOM的主要特征是它极大地减少了用户必须编写的代码数量,JDOM的应用程序的程度通常是DOM应用程序的1/3,大约是SAX应用程序的一半。JDOM并不做所有的事情,只满足大多数用户要做的解析需求

    JAXP解析

    ​ JAXP出现是SUN公司为了弥补JAVA在XML标准制定上的空白而制定的一套JAVA XML标准API。它并没有为JAVA解析XML提供任何新功能,但是它为在JAVA获取SAX与DOM解析器提供了更加直接的途径。

    ​ 它封装了sax\dom两种接口,并在sax\dom的基础之上,作了一套比较简单的api以供开发。

    XML文件解析器设计与实现(C++)

    ​ 造轮子!造轮子!

    ​ XML文件解析器实现代码参考来源:https://github.com/oldjun/yazi-xml

    XML文件解析器用法
    • 从文件加载XML
    • 从字符串加载XML
    • 访问节点:根据数组下标,节点名称
    • 遍历节点:数组,迭代器,for in
    • 添加节点
    • 删除节点
    • 获取,修改节点名称
    • 获取,删除节点属性
    • 获取,修改节点内容
    XML解析器具体实现

    XML文件解析器具体实现取决于xml类和Parser类,在xml.h文件中,增加了一个Value数值转化类,方便于XML节点属性的自动转化

    xml.h文件
    #pragma once
    
    #include 
    #include 
    #include 
    using namespace std;
    
    namespace wzh {
        namespace xml {
    
            class Value {
                // 数值转化类
                public:
                    Value();
                    Value(bool value);
                    Value(int value);
                    Value(double value);
                    Value(const char * value);
                    Value(const string & value);
                    ~Value();
    
                    Value & operator = (bool value);
                    Value & operator = (int value);
                    Value & operator = (double value);
                    Value & operator = (const char * value);
                    Value & operator = (const string & value);
                    Value & operator = (const Value & value);
    
                    bool operator == (const Value & other);
                    bool operator != (const Value & other);
    
                    operator bool();
                    operator int();
                    operator double();
                    operator string();
    
                private:
                    string m_value;
            };
    
            class XML {
    
                public:
                    XML();
                    XML(const char * name);
                    XML(const string & name);
                    XML(const XML & other);
    
                    string name() const; // 获取节点名称
                    void setName(const string & name); // 设置节点名称
    
                    string text() const; // 获取节点内容
                    void setText(const string & text); // 设置节点内容
    
                    Value & attr(const string & key); // 获取节点属性
                    void setAttr(const string & key, const Value & val); // 设置节点属性
    
                    string str() const; // 返回属性序列化结果
    
                    void clear(); // 释放内存
    
                    void append(const XML & child); // 添加子节点
    
                    XML & operator [] (int index); // 以数组下标方式获取子节点
                    XML & operator [] (const char * name); // 通过节点名称获取子节点(支持C形式)
                    XML & operator [] (const string & name); // 通过节点名称获取子节点(支持C++形式)
    
                    XML & operator = (const XML & other);
    
                    void remove(int index); // 以数组下标方式删除子节点
                    void remove(const char * name); // 通过节点名称删除子节点(支持C形式)
                    void remove(const string & name); // 通过节点名称删除子节点(支持C++形式)
    
                    typedef std::list::iterator iterator;
    
                    iterator begin() {
                        return m_child->begin();
                    }
    
                    iterator end() {
                        return m_child->end();
                    }
    
                    iterator erase(iterator it) {
                        it->clear();
                        return m_child->erase(it);
                    }
    
                    int size() const {
                        return m_child->size();
                    }
    
                    bool load(const string & filename);
    
                    bool save(const string & filename);
                    
                    bool parse(const string & str);
     
    
                private:
                    // 使用指针加快数据的读取以及存储开销
                    string* m_name; // 节点名称
                    string* m_text; // 节点内容
                    std::map* m_attrs; // 节点属性
                    std::list* m_child; // 子节点
            };
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    xml.cpp文件
    #include "xml.h"
    #include "Parser.h"
    using namespace wzh::xml;
    
    #include 
    #include 
    using namespace std;
    
    Value::Value() {
    }
    
    Value::Value(bool value) {
        *this = value;
    }
    
    Value::Value(int value) {
        *this = value;
    }
    
    Value::Value(double value) {
        *this = value;
    }
    
    Value::Value(const char * value) : m_value(value) {
    }
    
    Value::Value(const string & value) : m_value(value) {
    }
    
    Value::~Value() {
    }
    
    Value & Value::operator = (bool value) {
        m_value = value ? "true" : "false";
        return *this;
    }
    
    Value & Value::operator = (int value) {
        stringstream ss;
        ss << value;
        m_value = ss.str();
        return *this;
    }
    
    Value & Value::operator = (double value) {
        stringstream ss;
        ss << value;
        m_value = ss.str();
        return *this;
    }
    
    Value & Value::operator = (const char * value) {
        m_value = value;
        return *this;
    }
    
    Value & Value::operator = (const string & value) {
        m_value = value;
        return *this;
    }
    
    Value & Value::operator = (const Value & value) {
        m_value = value.m_value;
        return *this;
    }
    
    bool Value::operator == (const Value & other) {
        return m_value == other.m_value;
    }
    
    bool Value::operator != (const Value & other) {
        return !(m_value == other.m_value);
    }
    
    Value::operator bool() {
        if (m_value == "true")
            return true;
        else if (m_value == "false")
            return false;
        return false;
    }
    
    Value::operator int() {
        return std::atoi(m_value.c_str());
    }
    
    Value::operator double() {
        return std::atof(m_value.c_str());
    }
    
    Value::operator string() {
        return m_value;
    }
    
    XML::XML() : m_name(nullptr), m_text(nullptr), m_attrs(nullptr), m_child(nullptr) {
    
    }
    
    XML::XML(const char * name) : m_name(nullptr), m_text(nullptr), m_attrs(nullptr), m_child(nullptr) {
        m_name = new string(name);
    }
    
    XML::XML(const string & name) : m_name(nullptr), m_text(nullptr), m_attrs(nullptr), m_child(nullptr) {
        m_name = new string(name);
    }
    
    XML::XML(const XML & other) {
        m_name = other.m_name;
        m_text = other.m_text;
        m_attrs = other.m_attrs;
        m_child = other.m_child;
    }
    
    string XML::name() const {
        if(m_name == nullptr) {
            return "";
        }
        return *m_name; 
    }
    
    void XML::setName(const string & name) {
        if(m_name != nullptr) {
            delete m_name;
            m_name = nullptr;
        }
        m_name = new string(name);
    }
    
    string XML::text() const {
        if(m_text == nullptr) {
            return "";
        }
        return *m_text;
    }
                    
    void XML::setText(const string & text) {
        if(m_text != nullptr) {
            delete m_text;
            m_text = nullptr;
        }
        m_text = new string(text);
    }
    
    Value & XML::attr(const string & key) {
        if(m_attrs == nullptr) {
            m_attrs = new std::map();
        }
        return (*m_attrs)[key];
    }
                    
    void XML::setAttr(const string & key, const Value & val) {
        if(m_attrs == nullptr) {
            m_attrs = new std::map();
        }
        (*m_attrs)[key] = val;
    }
    
    string XML::str() const {
        if(m_name == nullptr) {
            throw std::logic_error("element name is empty");
        }
    
        stringstream ss;
        ss << "<";
        ss << *m_name;
        if(m_attrs != nullptr) {
            for(auto it = m_attrs->begin(); it != m_attrs->end(); it++) {
                ss << " " << it->first << "=\"" << (string)it->second << "\"";
            }
        }
        ss << ">";
        if(m_child != nullptr) {
            for(auto it = m_child->begin(); it != m_child->end(); it++) {
                ss << it->str();
            }
        }
        if(m_text != nullptr) {
            ss << *m_text;
        }
        ss << "";
        return ss.str();
    }
    
    void XML::clear() {
        if(m_name != nullptr) {
            delete m_name;
            m_name = nullptr;
        }
        if(m_text != nullptr) {
            delete m_text;
            m_text = nullptr;
        }
        if(m_attrs != nullptr) {
            delete m_attrs;
            m_attrs = nullptr;
        }
        if(m_child != nullptr) {
            for(auto it = m_child->begin(); it != m_child->end(); it++) {
                it -> clear();
            }
            delete m_child;
            m_child = nullptr;
        }
    }
    
    void XML::append(const XML & child) {
        if(m_child == nullptr) {
            m_child = new std::list();
        }
        m_child->push_back(child);
    }
    
    XML & XML::operator [] (int index) {
        if(index < 0) {
            throw std::logic_error("index less than zero");
        }
        if(m_child == nullptr) {
            m_child = new std::list();
        }
        int size = m_child->size();
        if(index >= 0 && index < size) {
            auto it = m_child->begin();
            for(int i=0; i= size) {
            for(int i=size; i<=index; i++)
                m_child->push_back(XML());
        }
        return m_child->back();
    }
    
    XML & XML::operator [] (const char * name) {
        return (*this)[string(name)];
    }
    
    XML & XML::operator [] (const string & name) {
        if(m_child == nullptr) {
            m_child = new std::list();
        }
        for(auto it = m_child->begin(); it != m_child->end(); it++) {
            if(it->name() == name) {
                return *it;
            }
        }
        m_child->push_back(XML(name));
        return m_child->back();
    }
    
    XML & XML::operator = (const XML & other) {
        clear();
        m_name = other.m_name;
        m_text = other.m_text;
        m_attrs = other.m_attrs;
        m_child = other.m_child;
        return *this;
    }
    
    void XML::remove(int index) {
        if(m_child == nullptr) {
            return;
        }
        int size = m_child->size();
        if(index < 0 || index >= size) {
            throw std::logic_error("index out of range");
            return;
        }
        auto it = m_child->begin();
        for(int i=0; iclear();
        m_child->erase(it);
    }
    
    void XML::remove(const char * name) {
        remove(string(name));
    }
    
    void XML::remove(const string & name) {
        if(m_child == nullptr) {
            return;
        }
        for(auto it = m_child->begin(); it != m_child->end();) {
            if(it->name() == name) {
                it->clear();
                it = m_child->erase(it);
            }
            else {
                it++;
            }
        }
    }
    
    bool XML::load(const string & filename)
    {
        Parser p;
        if (!p.load_file(filename))
        {
            return false;
        }
        *this = p.parse();
        return true;
    }
    
    bool XML::save(const string & filename)
    {
        ofstream fout(filename);
        if (fout.fail())
        {
            return false;
        }
        fout << str();
        fout.close();
        return true;
    }
    
    bool XML::parse(const string & str)
    {
        Parser p;
        if (!p.load_string(str))
        {
            return false;
        }
        *this = p.parse();
        return true;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    Parser.h文件
    #pragma once
    
    #include "xml.h"
    #include 
    using namespace std;
    
    namespace wzh {
        namespace xml{
    
            class Parser {
    
                public:
                    Parser();
    
                    bool load_file(const string & file);
                    bool load_string(const string & str);
    
                    XML parse(); // 解析方法
    
                private:
                    void skip_white_space(); // 忽略空白符
    
                    bool parse_declaration(); // 解析声明
    
                    bool parse_comment(); // 解析注释
    
                    XML parse_element(); // 解析主函数
    
                    string parse_element_name(); // 解析节点名称
    
                    string parse_element_text(); // 解析节点文本
    
                    string parse_element_attr_key(); // 解析节点属性
    
                    string parse_element_attr_val(); // 解析节点属性值
    
                private:
                    string m_str;
                    int m_idx;
    
            };
    
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    Parser.cpp文件
    #include "Parser.h"
    using namespace wzh::xml;
    
    #include 
    #include 
    #include 
    using namespace std;
    
    Parser::Parser() : m_str(""), m_idx(0) {
    }
    
    bool Parser::load_string(const string & str) {
        m_str = str;
        m_idx = 0;
        return true;
    }
    
    bool Parser::load_file(const string & filename) {
        ifstream fin(filename);
        if(fin.fail()) {
            return false;
        }
        stringstream ss;
        ss << fin.rdbuf();
        m_str = ss.str();
        m_idx = 0;
        return true;
    }
    
    void Parser::skip_white_space() {
        while(m_str[m_idx] == ' ' || m_str[m_idx] == '\r' || m_str[m_idx] == '\n' || m_str[m_idx] == '\t') {
            m_idx++;
        }
    }
    
    XML Parser::parse() {
        skip_white_space();
        if(m_str.compare(m_idx, 5, "", m_idx);
        if(pos == std::string::npos) {
            return false;
        }
        m_idx = pos + 2;
        return true;
    }
    
    bool Parser::parse_comment() {
        if(m_str.compare(m_idx, 4, "", m_idx);
        if(pos == std::string::npos) {
            return false;
        }
        m_idx = pos + 3;
        return true;
    }
    
    XML Parser::parse_element() {
        XML elem;
        m_idx++;
        skip_white_space();
        const string & name = parse_element_name();
        elem.setName(name);
        while(m_str[m_idx] != '\0') {
            skip_white_space();
            if(m_str[m_idx] == '/') {
                if(m_str[m_idx+1] == '>') {
                    m_idx += 2;
                    break;
                }
                else {
                    throw logic_error("xml empty element is error");
                }
            }
            else if(m_str[m_idx] == '>') {
                m_idx++;
                string text = parse_element_text();
                if(text != "") {
                    elem.setText(text);
                }
            }
            else if(m_str[m_idx] == '<') {
                if(m_str[m_idx+1] == '/') {
                    // 标签
                    string end_tag = "";
                    size_t pos = m_str.find(end_tag, m_idx);
                    if (pos == std::string::npos) {
                        throw std::logic_error("xml element " + name + " end tag not found");
                    }
                    m_idx = (pos + end_tag.size());
                    break;
                }
                else if (m_str.compare(m_idx, 4, "