初步接触TinyXML2

写这篇文章纯属偶然。
我很少接触xml,工作上也没使用过。即使使用,也是别人做好的接口,直接调用。最近的一个项目中,因为某些策略问题,造成项目的代码十分混乱,东拼西凑,做成了个不像样的东西。其中有部分是设备端与PC端的网络协议传输,使用到了自定义的xml协议,因为某些原因,这个协议不断地改进但未同步更新,幸亏做了兼容,但部分新加协议未在设备端更新,所以要添加进去,不过某同事忙于其它事务,所以只有我上了(个中故事等有空写篇文章出来)。

闲话少说,为了研究一下xml,自己抽空练习使用了一下tinyxml,为了避免与公司发生不必要的纠纷,特别使用了tinyxml第二版本(公司代码使用第一版本),特地在周末写程序。
tinyxml的第二版本改动很多,参考官方网站说明。
在使用前要包含头文件并要使用tinyxml2命令空间。如下:

1
2
#include "tinyxml2.h"
using namespace tinyxml2;

下面给出创建xml的示例函数:

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
// 创建xml示例
int createXML(const char* xmlFile)
{
XMLDocument* doc = new XMLDocument();
if (doc == NULL)
{
return -1;
}

// dec
XMLDeclaration *dec = NULL;
dec = doc->NewDeclaration("xml version=\"1.0\" encoding=\"gb2312\" standalone=\"yes\"");
//dec = doc->NewDeclaration(); // 默认为utf-8
if (dec == NULL)
{
return -1;
}

doc->LinkEndChild(dec);

////////////////////////////////
// 根元素
XMLElement* root = doc->NewElement("Root");
if (root == NULL)
{
return -1;
}
root->SetAttribute("ver", "1.0");
doc->LinkEndChild(root);
////////////////////////////////
// 第一级子节点
XMLElement* fstEle = doc->NewElement("subClass1");
XMLText* fstText = doc->NewText("Text1");
fstEle->SetAttribute("attribute1", "foo");
fstEle->SetAttribute("attribute2", "bar");
fstEle->LinkEndChild(fstText);
root->LinkEndChild(fstEle);

// 第二个同级节点
XMLElement* fstEle1 = doc->NewElement("subClass2");
XMLText* fstText1 = doc->NewText("Text2");
fstEle1->SetAttribute("attribute1", "foo");
fstEle1->SetAttribute("attribute2", "bar");
fstEle1->LinkEndChild(fstText1);
root->LinkEndChild(fstEle1);

// 第三个同级节点,但没有Text,可以再设一级子节点
XMLElement* thdEle = doc->NewElement("subClass3");
thdEle->SetAttribute("attribute1", "foo");
root->LinkEndChild(thdEle);

// 第三个节点的子节点
XMLElement* sndEle = doc->NewElement("sub_subClass1");
XMLText* sndText = doc->NewText("subText");
sndEle->SetAttribute("attribute", "foobar");
sndEle->LinkEndChild(sndText);
thdEle->LinkEndChild(sndEle);

// 第四个节点
XMLElement* fstEle2 = doc->NewElement("subClass4");
XMLText* fstText2 = doc->NewText("Text4");
fstEle2->SetAttribute("attribute1", "foo");
fstEle2->SetAttribute("attribute2", "bar");
fstEle2->LinkEndChild(fstText2);
root->LinkEndChild(fstEle2);

///////////////////////////////////
// 保存,打印
doc->SaveFile(xmlFile);

XMLPrinter printer;
doc->Print(&printer);
const char* xmlcstr = printer.CStr();
// 打印
printf("xml buffer: \n");
printf("%s\n", xmlcstr);

///////////////////
#if 0
XMLElement* rootEle = NULL;
XMLElement* node = NULL;
rootEle = doc->RootElement();
if (rootEle == NULL) return -1;

findNode(rootEle, "sub_subClass1", node);

// text
const char* text = NULL;
findText(node, &text);
printf("---- text: %s\n", text);

const char* test_value = NULL;
findAttribute(node, "apple", test_value);
printf("xxxxx: %s\n", test_value);
#endif

delete doc;
doc = NULL;

return 0;
}

结果如下:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="gb2312" standalone="yes"?>
<Root ver="1.0">
<subClass1 attribute1="foo" attribute2="bar">Text1</subClass1>
<subClass2 attribute1="foo" attribute2="bar">Text2</subClass2>
<subClass3 attribute1="foo">
<sub_subClass1 attribute="foobar">subText</sub_subClass1>
</subClass3>
<subClass4 attribute1="foo" attribute2="bar">Text4</subClass4>

下面再给出解析xml内容的示例函数:

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
int findElement(XMLElement* root)
{
if (root == NULL) return -1;
XMLElement* ele = NULL;
int i = 0;
for (ele = root->FirstChildElement(); ele; ele = ele->NextSiblingElement())
{
printf("Element: %s Text: %s\n", ele->Value(), ele->GetText());
const XMLAttribute* attr = NULL;
for (attr = ele->FirstAttribute(), i = 0; attr; attr = attr->Next(), i++)
{
printf("attr(%d) (%s: %s)\n", i, attr->Name(), attr->Value());
}

if (ele->FirstChildElement())
{
printf("sub %s: \n", ele->Value());
findElement(ele); // 递归查找元素
}
}
return 0;
}

// 解析xml示例
int parseXML(const char* xmlFile)
{
XMLDocument* doc = new XMLDocument();
if (doc == NULL)
{
return -1;
}

doc->LoadFile(xmlFile);
doc->Print();

// 声明
//todo

// 根
XMLElement* root = doc->RootElement();
if (root == NULL) return -1;

// 元素属性
const XMLAttribute* attr = NULL;
for (attr = root->FirstAttribute(); attr; attr = attr->Next())
{
printf("root: %s attr (%s: %s)\n", root->Value(), attr->Name(), attr->Value());
}
// 查找元素并打印
findElement(root);
return 0;
}

结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
root: Root attr (ver: 1.0)
Element: subClass1 Text: Text1
attr(0) (attribute1: foo)
attr(1) (attribute2: bar)
Element: subClass2 Text: Text2
attr(0) (attribute1: foo)
attr(1) (attribute2: bar)
Element: subClass3 Text: (null)
attr(0) (attribute1: foo)
sub subClass3:
Element: sub_subClass1 Text: subText
attr(0) (attribute: foobar)
Element: subClass4 Text: Text4
attr(0) (attribute1: foo)
attr(1) (attribute2: bar)

tinyxml2的资料比较少,官方的示例及文档是比较权威的,可以参考一下。根据我的使用及查阅的资料,下面列举与第一版本改动的地方。
1、源文件个数减少至2个,即一个头文件(tinyxml2.h),一个实现文件(tinyxml2.cpp)。
2、加入了tinyxml2的命名空间。
3、对类名称作了修改,将TiXml**改为XML**,比如TiXmlElement改为XMLElement
4、少了很多new,只有XMLDocument需要new,其它均由该类的方法进行封装。如下:
旧:

1
TiXmlDeclaration *dec= new TiXmlDeclaration("1.0","","");

新:

1
XMLDeclaration *dec = doc->NewDeclaration("xml version=\"1.0\" encoding=\"gb2312\" standalone=\"yes\"");

旧:

1
TiXmlElement *ele= new TiXmlElement("Root");

新:

1
XMLElement* root = doc->NewElement("Root");

5、网上有人讨论是否需要手动delete掉new出来的类,我没有在实际工作中使用到,所以不发表意见。在第二版中,示例代码只有XMLDocument才用delete,其它没有使用。所以上述代码也在最后delete。

资源:
tinyxml网站:http://www.grinninglizard.com/tinyxml2/
git仓库地址:https://github.com/leethomason/tinyxml2.git

迟,于2013年9月8日午后