Tinyxml库

许多公司常常使用XML文件保存信息,因此,需要使用专门XML库获取数据。

常用的XML库

Expat :用C语言编写的xml解析库

Libxml2 :Gnome的xml C解析器和工具包

libxml++ :C++的xml解析器

PugiXML :用于C++的,支持XPath的轻量级,简单快速的XML解析器。

RapidXml :试图创建最快速的XML解析器,同时保持易用性,可移植性和合理的W3C兼容性。

TinyXML :简单小型的C++XML解析器,可以很容易地集成到其它项目中。

TinyXML2:简单快速的C++CML解析器,可以很容易集成到其它项目中。

TinyXML++:TinyXML的一个全新的接口,使用了C++的许多许多优势,模板,异常和更好的异常处理。

Xerces-C++ :用可移植的C++的子集编写的XML验证解析器。

介绍

XML 全称为可扩展标记语言(eXtensible Markup Language),是一种用于标记电子文件结构以便存储、传输和展现数据的标记语言。XML 被设计用来传输和存储数据,而不是用来显示数据的。

XML 的解析方式分为四种:1、DOM 解析;2、SAX 解析;3、JDOM 解析;4、DOM4J 解析。其中前两种属于基础方法,是官方提供的平台无关的解析方式;后两种属于扩展方法,它们是在基础的方法上扩展出来的,只适用于 Java 平台。

DOM 解析

DOM 的全称是 Document Object Model,即文档对象模型。在应用程序中,基于 DOM 的 XML 分析器将一个 XML 文档转换成一个对象模型的集合(通常称 DOM 树)应用程序正是通过对这个对象模型的操作,来实现对 XML 文档数据的操作。通过 DOM 接口,应用程序可以在任何时候访问 XML 文档中的任何一部分数据,因此,这种利用 DOM 接口的机制也被称作随机访问机制。

DOM 接口提供了一种通过分层对象模型来访问 XML 文档信息的方式,这些分层对象模型依据 XML 的文档结构形成了一棵节点树。无论 XML 文档中所描述的是什么类型的信息,即便是制表数据、项目列表或一个文档,利用 DOM 所生成的模型都是节点树的形式。也就是说,DOM 强制使用树模型来访问 XML 文档中的信息。由于 XML 本质上就是一种分层结构,所以这种描述方法是相当有效的。

DOM 树所提供的随机访问方式给应用程序的开发带来了很大的灵活性,它可以任意地控制整个 XML 文档中的内容。然而,由于 DOM 分析器把整个 XML 文档转化成 DOM 树放在了内存中,因此,当文档比较大或者结构比较复杂时,对内存的需求就比较高。而且,对于结构复杂的树的遍历也是一项耗时的操作。所以,DOM 分析器对机器性能的要求比较高,实现效率不十分理想。不过,由于 DOM 分析器所采用的树结构的思想与 XML 文档的结构相吻合,同时鉴于随机访问所带来的方便,因此,DOM 分析器还是有很广泛的使用价值的。

优点:

  • 形成了树结构,有助于更好的理解、掌握,且代码容易编写。
  • 解析过程中,树结构保存在内存中,方便修改。

缺点:

  • 由于文件是一次性读取,所以对内存的耗费比较大。
  • 如果XML文件比较大,容易影响解析性能且可能会造成内存溢出。

XML解析和Json解析简介以及两者的区别

准备工作

首先看一下XML文件的组成部分:

xml主要是由两部分组成,头部信息节点

细分下来,头部信息就是开头这一串东西:<?xml version="1.0" encoding="utf-8" ?>

节点是:根节点、子节点、属性三部分。

image-20240518204145695

一个完整的xml文件由图中部分组成!

头部信息:这个是xml文件必须要有的一段描述语句;

根节点:xml文件由只有一个根节点(仅有),它是所有子节点的父节点;

子节点:可有多个,必须在根节点内;

属性:存储数据的一种方式。

下载链接:https://sourceforge.net/projects/tinyxml/

在Linux环境下,我直接放在XMLParse下,共四个文件。要用直接导入头文件即可。

创建

创建一个xml文件,并为其插入头部信息 和 跟、子节点。

创建一个XML类

1
TiXmlDocument* tinyXmlDoc = new TiXmlDocument();

创建头部信息

并插入到xml类中

1
2
3
4
// xml的声明(三个属性:版本,编码格式,保留空串即可)
TiXmlDeclaration* tinyXmlDeclare = new TiXmlDeclaration("1.0", "utf-8", ""); // 声明头部格式
// 插入文档类中
tinyXmlDoc->LinkEndChild(tinyXmlDeclare);

即上图头部信息

创建根节点

1
2
3
// 创建时需要指定根节点的名称
TiXmlElement* Library = new TiXmlElement("Library");
tinyXmlDoc->LinkEndChild(Library); // 把根节点插入到文档类中

即上图Library

添加子节点

首先创建一个子节点,然后新建一个文本,将文本添加到子节点中,最后将字节点添加到根节点中。

1
2
3
4
TiXmlElement *Book = new TiXmlElement("Book");
TiXmlText *bookText = new TiXmlText("书本"); // 创建文本
Book->LinkEndChild(bookText); // 给Book节点添加文本
Library->LinkEndChild(Book); // 插入到根节点下

添加带属性的子节点

使用setAttribute可以简单的给节点添加属性值;
如果要创建子节点的子节点,就跟创建子节点一样,只是创建好后要添加到子节点中即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TiXmlElement *Book1 = new TiXmlElement("Book1");
// 插入属性
Book1->SetAttribute("ID", 1);
Book1->SetAttribute("Name", "水浒传");
Book1->SetAttribute("Price", "64.6");

// 创建Book1的子节点Description
TiXmlElement *Description = new TiXmlElement("Description");
TiXmlText *descriptionText = new TiXmlText("108个拆迁户"); // 创建文本
Description->LinkEndChild(descriptionText); // 给Description节点添加文本
Book1->LinkEndChild(Description); // 插入到Book1节点下

// 创建Book1的子节点Page
TiXmlElement *Page = new TiXmlElement("Page");
TiXmlText *pageText = new TiXmlText("100页"); // 创建文本
Page->LinkEndChild(pageText); // 给Page节点添加文本
Book1->LinkEndChild(Page); // 插入到Book1节点下

Library->LinkEndChild(Book1); // 插入到根节点下

image-20240518204707541

将doc写入xml文件

只需使用文档类调用SaveFile方法即可,参数传文件名

写入成功返回true,写入失败返回false

1
bool result = tinyXmlDoc->SaveFile(FILE_NAME);

转换为字符串

1
2
3
TiXmlPrinter printer;
tinyXmlDoc->Accept(&printer);
printf("%s\n", printer.CStr());

如果只是想在控制台打印而已,那么可以直接调用函数tinyXmlDoc->Print();就可以实现在控制台上打印出来了!

创建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
void create_XML() {
printf("\n----- create_XML -----\n");

//新建一个xml文件
// 定义一个TiXmlDocument类指针
TiXmlDocument* tinyXmlDoc = new TiXmlDocument();



// xml的声明(三个属性:版本,编码格式,保留空串即可)
TiXmlDeclaration* tinyXmlDeclare = new TiXmlDeclaration("1.0", "utf-8", ""); // 声明头部格式
// 插入文档类中
tinyXmlDoc->LinkEndChild(tinyXmlDeclare);



// 根节点
TiXmlElement* Library = new TiXmlElement("Library");
tinyXmlDoc->LinkEndChild(Library); // 把根节点插入到文档类中



// 创建Book节点
TiXmlElement *Book = new TiXmlElement("Book");
TiXmlText *bookText = new TiXmlText("书本"); // 创建文本
Book->LinkEndChild(bookText); // 给Book节点添加文本
Library->LinkEndChild(Book); // 插入到根节点下


// 创建Book1节点
TiXmlElement *Book1 = new TiXmlElement("Book1");
// 插入属性
Book1->SetAttribute("ID", 1);
Book1->SetAttribute("Name", "水浒传");
Book1->SetAttribute("Price", "64.6");

// 创建Book1的子节点Description
TiXmlElement *Description = new TiXmlElement("Description");
TiXmlText *descriptionText = new TiXmlText("108个拆迁户"); // 创建文本
Description->LinkEndChild(descriptionText); // 给Description节点添加文本
Book1->LinkEndChild(Description); // 插入到Book1节点下

// 创建Book1的子节点Page
TiXmlElement *Page = new TiXmlElement("Page");
TiXmlText *pageText = new TiXmlText("100页"); // 创建文本
Page->LinkEndChild(pageText); // 给Page节点添加文本
Book1->LinkEndChild(Page); // 插入到Book1节点下

Library->LinkEndChild(Book1); // 插入到根节点下



// 保存到文件
bool result = tinyXmlDoc->SaveFile(FILE_NAME);
if (result == true) printf("文件写入成功!\n");
else printf("文件写入失败!\n");

// 打印出来看看
//tinyXmlDoc->Print();

TiXmlPrinter printer;
tinyXmlDoc->Accept(&printer);
printf("%s\n", printer.CStr());
}

添加

往xml文件中添加节点元素

例:在上面创建的xml文件基础上,添加两个Book1子节点。

定义一个xml文件类,并读取文件中的xml内容初始化它

这里有两种方式,有点奇怪,当使用第一种方式读取xml文件时,xml中有多个子节点Book1时,由于属性都有相同的id、name、price,导致会读取失败,不得已让我搞出了第二种方式去读取进行初始化,这样就没问题!

方式1

1
2
3
4
5
6
7
8
9
// 定义一个TiXmlDocument类指针
TiXmlDocument* tinyXmlDoc = new TiXmlDocument;

// 读取文件中的xml
if (!tinyXmlDoc->LoadFile(FILE_NAME)) {
// 读取失败,打印失败原因
printf("Could not load example xml file %s. Error='%s'\n", FILE_NAME, tinyXmlDoc->ErrorDesc());
return ;
}

方式2

定义xml文档类对象时指定文件名,然后在使用这个对象去调用LoadFile传参TIXML_ENCODING_LEGACY即可,具体传参是什么意思,我

也不清楚,自己摸索出来的,其中有三个枚举可以选择,其他两个都不行,就这个可以!

1
2
3
4
// 定义xml文档类对象时指定文件名
TiXmlDocument* tinyXmlDoc = new TiXmlDocument(FILE_NAME);
// 然后在使用这个对象去调用LoadFile传参TIXML_ENCODING_LEGACY即可
tinyXmlDoc->LoadFile(TIXML_ENCODING_LEGACY);

我一般都是,第一种方式用不了,就用第二种方式初始化!

对已存在的数据进行初始化

1
2
3
4
5
6
7
8
9
10
11
std::string data;	// 这里默认data是存储xml数据的字符串变量

/* 方式一: */
// 定义一个TiXmlDocument类指针
TiXmlDocument* tinyXmlDoc = new TiXmlDocument();
// 数据初始化
tinyXmlDoc->Parse(data.c_str());

/* 方式二: */
// 定义一个TiXmlDocument类指针
TiXmlDocument* tinyXmlDoc = new TiXmlDocument(data.c_str());

也可以获取头部信息中的版本号和编码

1
2
3
4
5
// 读取文档声明信息(也就是xml的头部信息:<?xml version="1.0" encoding="utf-8" ?>)
TiXmlDeclaration *pDeclar = tinyXmlDoc->FirstChild()->ToDeclaration();
if (pDeclar != NULL) {
printf("头部信息: version is %s , encoding is %s\n", pDeclar->Version(), pDeclar->Encoding());
}

获取根节点

这个定义是需要指定参数根节点的名字,然后再通过RootElement方法获取。

1
2
TiXmlElement *Library = new TiXmlElement("Library");
Library = tinyXmlDoc->RootElement();

创建Book1节点,并设置属性

1
2
3
4
5
TiXmlElement *Book1 = new TiXmlElement("Book1");
// 插入属性
Book1->SetAttribute("ID", 2);
Book1->SetAttribute("Name", "西游记");
Book1->SetAttribute("Price", "99.81");

创建Book1的子节点Description 和 Page

和上面创建的思路代码是一样的,创建好后在插入到Book1节点中就好了

1
2
3
4
5
6
7
8
9
10
11
// 创建Book1的子节点Description
TiXmlElement *Description = new TiXmlElement("Description");
TiXmlText *descriptionText = new TiXmlText("师徒四人"); // 创建文本
Description->LinkEndChild(descriptionText); // 给Description节点添加文本
Book1->LinkEndChild(Description); // 插入到Book1节点下

// 创建Book1的子节点Page
TiXmlElement *Page = new TiXmlElement("Page");
TiXmlText *pageText = new TiXmlText("81页"); // 创建文本
Page->LinkEndChild(pageText); // 给Page节点添加文本
Book1->LinkEndChild(Page); // 插入到Book1节点下

image-20240518205212632

将Book1节点添加到根节点中

1
Library->LinkEndChild(Book1);	// 插入到根节点下

将doc写入xml文件

1
2
3
4
// 保存到文件	
bool result = tinyXmlDoc->SaveFile(FILE_NAME);
if (result == true) printf("文件写入成功!\n");
else printf("文件写入失败!\n");

为已存在的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
void add_XML() {
printf("\n----- add_XML -----\n");

// 定义一个TiXmlDocument类指针
TiXmlDocument* tinyXmlDoc = new TiXmlDocument(FILE_NAME);
tinyXmlDoc->LoadFile(TIXML_ENCODING_LEGACY);

// 读取文档声明信息(也就是xml的头部信息:<?xml version="1.0" encoding="utf-8" ?>)
TiXmlDeclaration *pDeclar = tinyXmlDoc->FirstChild()->ToDeclaration();
if (pDeclar != NULL) {
printf("头部信息: version is %s , encoding is %s\n", pDeclar->Version(), pDeclar->Encoding());
}

// 得到文件根节点
TiXmlElement *Library = new TiXmlElement("Library");
Library = tinyXmlDoc->RootElement();


// 创建Book1节点
TiXmlElement *Book1 = new TiXmlElement("Book1");
// 插入属性
Book1->SetAttribute("ID", 2);
Book1->SetAttribute("Name", "西游记");
Book1->SetAttribute("Price", "99.81");

// 创建Book1的子节点Description
TiXmlElement *Description = new TiXmlElement("Description");
TiXmlText *descriptionText = new TiXmlText("师徒四人"); // 创建文本
Description->LinkEndChild(descriptionText); // 给Description节点添加文本
Book1->LinkEndChild(Description); // 插入到Book1节点下

// 创建Book1的子节点Page
TiXmlElement *Page = new TiXmlElement("Page");
TiXmlText *pageText = new TiXmlText("81页"); // 创建文本
Page->LinkEndChild(pageText); // 给Page节点添加文本
Book1->LinkEndChild(Page); // 插入到Book1节点下

Library->LinkEndChild(Book1); // 插入到根节点下




// 创建Book1节点
Book1 = new TiXmlElement("Book1");
// 插入属性
Book1->SetAttribute("ID", 3);
Book1->SetAttribute("Name", "三国演义");
Book1->SetAttribute("Price", "66.66");

// 创建Book1的子节点Description
Description = new TiXmlElement("Description");
descriptionText = new TiXmlText("三国大战"); // 创建文本
Description->LinkEndChild(descriptionText); // 给Description节点添加文本
Book1->LinkEndChild(Description); // 插入到Book1节点下

// 创建Book1的子节点Page
Page = new TiXmlElement("Page");
pageText = new TiXmlText("30页"); // 创建文本
Page->LinkEndChild(pageText); // 给Page节点添加文本
Book1->LinkEndChild(Page); // 插入到Book1节点下

Library->LinkEndChild(Book1); // 插入到根节点下


// 保存到文件
bool result = tinyXmlDoc->SaveFile(FILE_NAME);
if (result == true) printf("文件写入成功!\n");
else printf("文件写入失败!\n");

// 打印出来看看
tinyXmlDoc->Print();
}

可以看到,创建相同子节点的代码都是一样的,也就是说,当需要创建几十个这个的子节点时,可以使用for循环去处理,至于数据可以使用数组是事先存储即可!

解析

解析指定节点中值

定义一个xml文件类,并读取文件中的xml内容初始化它

和上面 添加 的第1步骤 一样…

获取根节点

和上面 三、添加 的第2步骤 一样…

解析Book节点

当然,前面已将Book节点删掉了,所以获取到的Book为NULL

所以如果是指针,在使用前最好先判断一下再去使用!

1
2
3
4
TiXmlElement* Book = Library->FirstChildElement("Book");
if (Book) {
printf("Book = %s\n\n", Book->GetText());
}

解析所有的Book1节点的属性

直接调用Attribute,传入属性名即可获得属性的值;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* 当有多个相同名字的节点时,可以使用循环进行读取解析 */
// 函数FirstChildElement() : 找到指定名字的元素
// 函数NextSiblingElement : 在同一级元素中查找下一个指定名字的元素
TiXmlElement* pItem = Library->FirstChildElement("Book1");
if (pItem) {
for (; pItem != NULL; pItem = pItem->NextSiblingElement("Book1")) {

// 解析属性
printf("ID = %s\n", pItem->Attribute("ID"));
printf("Name = %s\n", pItem->Attribute("Name"));
printf("Price = %s\n", pItem->Attribute("Price"));

printf("\n\n");
}
}

解析所有的Book1节点的子节点

获得对应解析的节点后,调用GetText可以获得里面的值;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/* 当有多个相同名字的节点时,可以使用循环进行读取解析 */
TiXmlElement* pItem = Library->FirstChildElement("Book1");
if (pItem) {
for (; pItem != NULL; pItem = pItem->NextSiblingElement("Book1")) {
// 解析Book1的子节点
TiXmlElement* Description = pItem->FirstChildElement("Description");
printf("Description = %s\n", Description->GetText());

TiXmlElement *Page = pItem->FirstChildElement("Page");
printf("Page = %s\n", Page->GetText());

printf("\n\n");
}
}

解析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
void parse_XML() {
printf("\n----- parse_XML -----\n");

/* 方式一:当读取有问题时,可以使用下面方式二

// 定义一个TiXmlDocument类指针
TiXmlDocument* tinyXmlDoc = new TiXmlDocument;

// 读取文件中的xml
if (!tinyXmlDoc->LoadFile(FILE_NAME)) {
// 读取失败,打印失败原因
printf("Could not load example xml file %s. Error='%s'\n", FILE_NAME, tinyXmlDoc->ErrorDesc());
return ;
}
*/

// 定义一个TiXmlDocument类指针
TiXmlDocument* tinyXmlDoc = new TiXmlDocument(FILE_NAME);
tinyXmlDoc->LoadFile(TIXML_ENCODING_LEGACY);

// 读取文档声明信息(也就是xml的头部信息:<?xml version="1.0" encoding="utf-8" ?>)
TiXmlDeclaration* pDeclar = tinyXmlDoc->FirstChild()->ToDeclaration();
if (pDeclar != NULL) {
printf("头部信息:version is %s , encoding is %s\n", pDeclar->Version(), pDeclar->Encoding());
}

// 得到文件根节点
TiXmlElement* Library = new TiXmlElement("Library");
if (Library) {
Library = tinyXmlDoc->RootElement();
}


// 解析Book节点
TiXmlElement* Book = Library->FirstChildElement("Book");
if (Book) {
printf("Book = %s\n\n", Book->GetText());
}



/* 当有多个相同名字的节点时,可以使用循环进行读取解析 */
// 函数FirstChildElement() : 找到指定名字的元素
// 函数NextSiblingElement : 在同一级元素中查找下一个指定名字的元素
TiXmlElement* pItem = Library->FirstChildElement("Book1");
if (pItem) {
for (; pItem != NULL; pItem = pItem->NextSiblingElement("Book1")) {

// 解析属性
printf("ID = %s\n", pItem->Attribute("ID"));
printf("Name = %s\n", pItem->Attribute("Name"));
printf("Price = %s\n", pItem->Attribute("Price"));

// 解析Book1的子节点
TiXmlElement* Description = pItem->FirstChildElement("Description");
printf("Description = %s\n", Description->GetText());

TiXmlElement *Page = pItem->FirstChildElement("Page");
printf("Page = %s\n", Page->GetText());

printf("\n\n");
}
}
}

参考资料