【前言】
XML即可扩展标记语言,在游戏开发中,常用于保存游戏数据信息,如最高分、游戏等级等信息,和描述一些资源等。加载动画的plist文件、瓦片地图编辑器到处的地图格式tmx文件,实际上都是特定格式的xml文件。另外 UserDefault 单例类保存的数据,也是存储在xml文件中的。Cocos2d-x 已经加入了 tinyxml2库 用于xml的解析。3.x版本位于external/tinyxml2下。
本节要介绍的就是:如何使用tinyxml2库来操作处理xml文件。
【XML简介】
摘自:http://www.w3school.com.cn/xml/index.asp
1、什么是XML?
> XML 指可扩展标记语言(EXtensible Markup Language)。
> XML 是一种标记语言,很类似 HTML。
> XML 的设计宗旨是:传输数据,而非显示数据。
> XML 标签没有被预定义,您需要自行定义标签。
> XML 被设计为具有自我描述性。
> XML 是 W3C 的推荐标准。
2、XML的一个例子
先来看一个简单的例子。
1
2
3
4
5
6
7
8
9
|
//
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<note>
<to>George</to>
<from>John</from>
<heading>Reminder</heading>
<body>Don't forget the meeting!</body>
</note>
//
|
代码分析:
(1)第一行是 XML 声明。它定义 XML 的版本(1.0)和所使用的编码(UTF-8字符集)。
(2)下一行描述文档的根元素(像在说:“本文档是一个便签”):<note> 。
(3)接下来 4 行描述根元素的 4 个子元素(to,from,heading,body)。
(4)最后一行定义根元素的结尾:</note> 。
以上是一个简单的XML文档。可以发现XML的语法很简单,标签没有被预定义,都是自己定义的标签。并且元素可以有子元素,这就形成了一个树形结构。
3、XML树结构
> XML 文档必须包含根元素,该元素是所有其他元素的父元素。
> 所有元素均可拥有 多个子元素。
> 所有元素均可拥有 文本内容和属性(类似 HTML 中)。
> 父、子以及同胞等术语用于描述元素之间的关系:父元素拥有子元素;相同层级上的子元素成为同胞(兄弟或姐妹)。
> XML 文档中的元素形成了一棵文档树的结构。这棵树从“根部”开始,并扩展到树的“枝叶”。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//
<root>
<child1>
<subchild1>.....</subchild1>
<subchild2>.....</subchild2>
.....
</child1>
<child2>
<subchild1>.....</subchild1>
<subchild2>.....</subchild2>
.....
</child2>
.....
</root>
//
|
如下所示,一个树结构的实例:
它表示了XML中的一本书:
> 根元素是 <bookstore>。文档中的所有 <book> 元素都被包含在 <bookstore> 中 。
> 并且<book> 元素也有 4 个子元素:<title>、< author>、<year>、<price> 。
> category、lang均为元素的属性。
> <book>的4个子元素。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
//
<bookstore>
<book category=
"COOKING"
>
<title>Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category=
"CHILDREN"
>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
<book category=
"WEB"
>
<title>Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
//
|
4、语法规则
(1)XML 文档必须有根元素。
(2)XML 文档必须有结束标签。( <p>This is a paragraph.</p> )
(3)XML 标签对大小写敏感。(标签 <Letter> 与 <letter> 是不同的)
(4)XML 元素必须被正确的嵌套。
1
2
3
4
5
6
|
//
<!-- 错误的嵌套 -->
<b><i>This text is bold and italic</b></i>
<!-- 正确的嵌套 -->
<b><i>This text is bold and italic</i></b>
//
|
(5)XML 属性值必须加引号。(单引号' '、双引号" " ,均可以)
1
2
3
4
|
//
<note date=
"08/08/2008"
>
<note date=
'08/08/2008'
>
//
|
4.1、注释
在 XML 中编写注释的语法与 HTML 的语法很相似:
1
2
3
|
//
<!-- This is a comment -->
//
|
4.2、保留空格
HTML会把多个连续的空格字符裁减(合并)为一个。
而在XML中,空格不会被删节。
1
2
3
4
5
|
//
content :Hello my name is David.
HTML :Hello my name is David.
XML :Hello my name is David.
//
|
4.3、实体引用
在 XML 中,一些字符拥有特殊的意义。
如果你把字符 "<" 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。
这样会产生 XML 错误:
1
|
<message>
if
salary < 1000 then</message>
|
为了避免这个错误,请用实体引用来代替 "<" 字符:
1
|
<message>
if
salary < 1000 then</message>
|
在 XML 中,有 5 个预定义的实体引用:
4.4、XML元素
XML 元素指的是从(且包括)开始标签,直到(且包括)结束标签的部分。
> 元素可包含:其他元素、文本、或者两者的混合物。
> 元素也可以拥有属性。
1
2
3
4
5
6
7
8
9
10
|
//
<bookstore>
<book category=
"CHILDREN"
>
<title>Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
<price>29.99</price>
</book>
</bookstore>
//
|
在上例中:
<bookstore> 和 <book> 都拥有元素内容,因为它们包含了其他元素。
<author> 只有文本内容,因为它仅包含文本。
在上例中,只有 <book> 元素拥有属性(category="CHILDREN")。
4.5、XML属性
XML 元素可以在开始标签中包含属性,类似 HTML。属性 (Attribute) 提供关于元素的额外(附加)信息。
属性通常提供:不属于数据组成部分的信息。
在下面的例子中,文件类型与数据无关,但是对需要处理这个元素的软件来说却很重要。
PS:属性的值必须加引号(单引号、双引号,均可以)。
1
2
3
|
//
<file type=
"gif"
>computer.gif</file>
//
|
4.6、元素 vs 属性
有时候属性和元素均可以提供相同的信息。
如下所示:
1
2
3
4
5
6
7
8
9
|
//
<person sex=
"female"
>
<name>Alice</name>
</person>
<person>
<sex>female</sex>
<name>Alice</name>
</person>
//
|
这样写虽然可以,但是这样的定义会造成数据的混乱,并且不易阅读(想获取数据信息,到底是访问属性还是元素?)
所以最好的做法是:
> 属性:用来提供不属于数据组成部分的信息。如图片格式、书籍分类、ID 索引等。
> 元素:用来描述数据信息。
4.7、元素的命名规则
XML 元素必须遵循以下命名规则:
> 名称可以含字母、数字以及其他的字符。
> 名称不能以数字或者标点符号开始。
> 名称不能以字符 “xml”(或者 XML、Xml)开始。
> 名称不能包含空格。
可使用任何名称,没有保留的字词。
XML元素的命名习惯:
> 使名称具有描述性。使用下划线的名称也很不错。
> 名称应当比较简短,比如:<book_title>,而不是:<the_title_of_the_book>。
> 避免 "-" 字符。如果按照这样的方式命名:"first-name",一些软件会认为你需要提取第一个单词。
> 避免 "." 字符。如果按照这样的方式命名:"first.name",一些软件会认为"name"是对象"first"的属性。
> 避免 ":" 字符。冒号会被转换为命名空间来使用。
【tinyxml2】
Cocos2d-x 已经加入了 tinyxml2库 用于xml的解析。
3.x版本位于external/tinyxml2下。
0、相关类
XMLNode :表示一个节点,包含一般方法,如访问自节点、兄弟节点、编辑自身、编辑子节点。
XMLDocument :表示整个XML文档,不对应其中某个特定的节点。
XMLElement :表示元素节点,可以包含子节点XMLElement、和属性XMLAttribute。
XMLAttribute :表示一个元素的属性。
XMLText :表示文本节点。
XMLComment :表示注释。
XMLDeclaration :表示声明。
1、添加头文件
1
2
3
4
|
//
#include "tinyxml2/tinyxml2.h"
using
namespace
tinyxml2;
//
|
2、XML数据解析
XML文档如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//
<?xml version=
"1.0"
encoding=
"UTF-8"
?>
<person>
<student id=
"1111"
>
<name>Alice</name>
<age>20</age>
</student>
<teacher id=
"2222"
>
<name>Bob</name>
<age>30</age>
</teacher>
</person>
//
|
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
|
//
//[0] 文件路径
std::string path =
"/soft/cocos2d-x-3.4/projects/Demo34/Resources/testXML.xml"
;
//[1] 创建管理XML文档的对象:XMLDocument
XMLDocument* doc =
new
XMLDocument();
//[2] 解析xml文件
// 方式一:
// Data data = FileUtils::getInstance()->getDataFromFile(path.c_str());
// XMLError errorID = doc->Parse((const char*)data.getBytes());
// 方式二:
// std::string data = FileUtils::getInstance()->getStringFromFile(path.c_str());
// XMLError errorID = doc->Parse(data.c_str());
// 方式三:
XMLError errorID = doc->LoadFile(path.c_str());
//[3] 判断是否解析错误
if
(errorID != 0) {
CCLOG(
"Parse Error!"
);
return
;
}
//[4] 获取根元素 <person>
XMLElement* root = doc->RootElement();
//[5] 获取子元素信息
//[5.1] 遍历root的子元素 <student> , <teacher>
// FirstChildElement() : 获取 root 的第一个子元素
// NextSiblingElement() : 获取 chd 的下一个兄弟元素
for
(XMLElement* chd = root->FirstChildElement(); chd; chd = chd->NextSiblingElement()) {
//[5.2] 获取子元素名称
CCLOG(
"chd : %s"
, chd->Name());
//[5.3] 遍历子元素的属性 id
// FirstAttribute() : 获取 chd元素 的第一个属性
// Next() : 获取 chd元素的attr属性 的下一个属性
for
(
const
XMLAttribute* attr = chd->FirstAttribute(); attr; attr = attr->Next()) {
// Name() : 属性名称
// Value() : 属性值
CCLOG(
"chd_attr : %s , %s"
, attr->Name(), attr->Value());
}
// 也可以通过属性名称,来获取属性值
// CCLOG("id = %s", chd->Attribute("id"));
//[5.4] 遍历子元素chd的子元素 <name> , <age>
for
(XMLElement* e = chd->FirstChildElement(); e; e = e->NextSiblingElement()) {
// 子元素e 为文本内容
// GetText() : 文本内容
CCLOG(
"e : %s , %s"
, e->Name(), e->GetText());
}
}
//[6] 释放内存
delete
doc;
//
|
控制台输出结果:
3、XML数据存储
以上面解析的XML文档为例,我们通过代码来生成相应的XML文档,并保存到xml文件中。
生成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
|
//
//[0] 文件路径
std::string path =
"/soft/cocos2d-x-3.4/projects/Demo34/Resources/testXML.xml"
;
//[1] 创建管理XML文档的对象:XMLDocument
XMLDocument* doc =
new
XMLDocument();
// <!-- begin -->
//[2] 创建XML声明,并连接到XML文档中
XMLDeclaration* declare = doc->NewDeclaration(
"xml version=\"1.0\" encoding=\"UTF-8\""
);
doc->LinkEndChild(declare);
// 添加到文档尾部
//[3] 创建注释,并连接到XML文档中
XMLComment* comment = doc->NewComment(
"this is xml comment"
);
doc->LinkEndChild(comment);
//[4] 创建根节点,并连接到XML文档
XMLElement* root = doc->NewElement(
"person"
);
doc->InsertEndChild(root);
// 与LinkEndChild()功能相同
//[5] 创建两个子元素,并连接到root元素中,作为root的子节点
XMLElement* student = doc->NewElement(
"student"
);
XMLElement* teacher = doc->NewElement(
"teacher"
);
root->LinkEndChild(student);
// 添加到root子元素中
root->LinkEndChild(teacher);
// 添加到root子元素中
//[6] 设置/添加 <student>、<teacher> 的属性值
student->SetAttribute(
"id"
,
"1111"
);
teacher->SetAttribute(
"id"
,
"2222"
);
//[7] 创建子元素,并连接到<student>、<teacher>元素中,作为子节点
XMLElement* name1 = doc->NewElement(
"name"
);
XMLElement* name2 = doc->NewElement(
"name"
);
XMLElement* age1 = doc->NewElement(
"age"
);
XMLElement* age2 = doc->NewElement(
"age"
);
student->LinkEndChild(name1);
student->LinkEndChild(age1);
teacher->LinkEndChild(name2);
teacher->LinkEndChild(age2);
//[8] 创建文本内容,并添加到<name>、<age>元素中,作为文本内容
XMLText* name1_text = doc->NewText(
"Alice"
);
XMLText* name2_text = doc->NewText(
"Bob"
);
XMLText* age1_text = doc->NewText(
"20"
);
XMLText* age2_text = doc->NewText(
"30"
);
name1->LinkEndChild(name1_text);
name2->LinkEndChild(name2_text);
age1->LinkEndChild(age1_text);
age2->LinkEndChild(age2_text);
// <!-- ended -->
//[9] 保存XMLDocument数据到XML文档中
doc->SaveFile(path.c_str());
//[10] 释放内存
delete
doc;
//
|
运行程序后,生成的XML文档如下:
4、XML数据修改
以上面存储的XML文档为例,进行数据的修改操作。
原始XML文档数据如下:
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
|
//
//[0] 文件路径
std::string path =
"/soft/cocos2d-x-3.4/projects/Demo34/Resources/testXML.xml"
;
//[1] 创建管理XML文档的对象:XMLDocument
XMLDocument* doc =
new
XMLDocument();
//[2] 解析xml文件
XMLError errorID = doc->LoadFile(path.c_str());
//[3] 判断是否解析错误
if
(errorID != 0) {
CCLOG(
"Parse Error!"
);
return
;
}
//[4] 获取根元素 <person>
XMLElement* root = doc->RootElement();
//[5] 获取子元素 <student>、<teacher>
XMLElement* student = root->FirstChildElement();
XMLElement* teacher = student->NextSiblingElement();
// <!-- begin -->
//[6] 修改数据
//[6.1] 添加新元素 <city>
XMLElement* city = doc->NewElement(
"city"
);
XMLText* city_text = doc->NewText(
"北京"
);
city->LinkEndChild(city_text);
student->LinkEndChild(city);
//[6.2] 添加新属性 <type>
root->SetAttribute(
"type"
,
"学校人群"
);
//[6.3] 修改元素名称
student->SetName(
"学生"
);
//[6.4] 修改属性值
student->SetAttribute(
"id"
,
"9999"
);
//[6.5] 删除元素
root->DeleteChild(teacher);
// <!-- ended -->
//[7] 保存XMLDocument修改后的数据,到XML文档中
doc->SaveFile(path.c_str());
//[8] 释放内存
delete
doc;
//
|
运行程序后,修改后的XML文档如下:
【常用数据操作】
这里介绍一下常用的4个类的使用方法。
> XMLNode :表示一个节点,包含一般方法,如访问自节点、兄弟节点、编辑自身、编辑子节点。
> XMLDocument :表示整个XML文档,不对应其中某个特定的节点。
> XMLElement :表示元素节点,可以包含子节点XMLElement、和属性XMLAttribute。
> XMLAttribute :表示一个元素的属性。
1、XMLNode
表示一个节点,包含一般方法,如访问自节点、兄弟节点、编辑自身、编辑子节点。
PS:它是 XMLDocument、XMLElement、XMLAttribute 的父类。
常用方法如下:
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
|
//
// 获得该XMLNode节点所在的XMLDocument文档
XMLDocument* GetDocument();
// 获取'value'值
// Document : 空
// Element : 元素名称
// Comment : 注释内容
// Text : 文本内容
const
char
* Value();
// 设置Node节点的value值
void
SetValue(
const
char
* val);
// 获取关联节点
// 获取父节点
XMLNode* Parent();
// 获取第一个子节点,若没有返回null
// 获取最后一个子节点,若没有返回null
// 获取前一个兄弟节点
// 获取下一个兄弟节点
XMLNode* FirstChild();
XMLNode* LastChild();
XMLNode* PreviousSibling();
XMLNode* NextSibling();
// 获取第一个子元素
// 获取最后一个子元素
// 获取前一个兄弟元素
// 获取下一个兄弟元素
XMLElement* FirstChildElement();
XMLElement* LastChildElement();
XMLElement* PreviousSiblingElement();
XMLElement* NextSiblingElement();
// 插入子节点
// 放在最前面
XMLNode* InsertFirstChild(XMLNode* addThis);
// 放在最后面
XMLNode* InsertEndChild(XMLNode* addThis);
XMLNode* LinkEndChild(XMLNode* addThis) {
return
InsertEndChild(addThis); }
// 放在指定afterThis子节点的后面
XMLNode* InsertAfterChild(XMLNode* afterThis, XMLNode* addThis);
// 删除子节点
// 删除所有子节点
void
DeleteChildren();
// 删除指定node子节点
void
DeleteChild(XMLNode* node);
//
|
2、XMLDocument
表示整个XML文档,不对应其中某个特定的节点。
PS:父类为XMLNode,拥有父类所有的方法,这里不再赘述。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
//
// 解析xml串,需要先通过FileUtils类获取xml文件的内容串
XMLError Parse(
const
char
* xml);
// 解析xml文件
XMLError LoadFile(
const
char
* filename);
// 将XMLDocument的xml内容保存到filename文件中
XMLError SaveFile(
const
char
* filename);
// 获取根节点
XMLElement* RootElement() {
return
FirstChildElement(); }
// 新建
// 元素 <a></a>
XMLElement* NewElement(
const
char
* name);
// 注释 <!-- ... -->
XMLComment* NewComment(
const
char
* comment);
// 文本内容 hello world
XMLText* NewText(
const
char
* text);
// 声明 <? ... ?>
// 如:<?xml version="1.0" encoding="UTF-8"?>
XMLDeclaration* NewDeclaration(
const
char
* text=0);
// 删除
// 删除指定节点
void
DeleteNode(XMLNode* node) { node->_parent->DeleteChild( node ); }
//
|
3、XMLElement
表示元素节点,可以包含子节点XMLElement、和属性XMLAttribute。
PS:父类为XMLNode,拥有父类所有的方法,这里不再赘述。
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
|
//
// 获取/设置 元素名称,等价于Value
const
char
* Name() {
return
Value(); }
void
SetName(
const
char
* str) { SetValue( str, staticMem ); }
// 获取元素的文本内容,若没有返回空
const
char
* GetText()
const
;
// 获取指定名称属性的属性值
const
char
* Attribute(
const
char
* name);
// 字符串
bool
BoolAttribute(
const
char
* name);
// bool
int
IntAttribute(
const
char
* name );
// int
unsigned UnsignedAttribute(
const
char
* name);
// unsigned int
float
FloatAttribute(
const
char
* name );
// float
double
DoubleAttribute(
const
char
* name );
// double
// 获取第一个属性
const
XMLAttribute* FirstAttribute();
// 获取指定名称属性
const
XMLAttribute* FindAttribute(
const
char
* name);
// 设置属性
void
SetAttribute(
const
char
* name,
const
char
* value);
void
SetAttribute(
const
char
* name,
bool
value);
void
SetAttribute(
const
char
* name,
int
value);
void
SetAttribute(
const
char
* name, unsigned value);
void
SetAttribute(
const
char
* name,
double
value);
// 删除指定属性
void
DeleteAttribute(
const
char
* name);
//
|
4、XMLAttribute
表示一个元素的属性。
没有父类。
常用方法如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//
// 获取属性名称
const
char
* Name();
// 获取下一个属性
// 该属性对应的元素中,定义在该属性后面的属性
XMLAttribute* Next();
// 获取属性值
const
char
* Value();
bool
BoolValue();
int
IntValue();
unsigned
int
UnsignedValue();
float
FloatValue();
double
DoubleValue();
// 设置属性值
void
SetAttribute(
const
char
*value);
void
SetAttribute(
bool
value);
void
SetAttribute(
int
value);
void
SetAttribute(unsigned
int
value);
void
SetAttribute(
float
value);
void
SetAttribute(
double
value);
//
|
【参考】
http://www.w3school.com.cn/xml/index.asp (W3School)
http://cn.cocos2d-x.org/tutorial/show?id=1225 (【官方文档】Cocos2d-x xml解析)
http://blog.csdn.net/w18767104183/article/details/28856829 (TinyXml 解析 XML)