Cocos数据篇[3.4](3) ——X

发布时间:2019-09-15 09:59:30编辑:auto阅读(1421)

    【唠叨】

        XML 即 可扩展标记语言,在游戏开发中,常用于保存游戏数据信息,如最高分、游戏等级等信息,和描述一些资源等。

        加载动画的plist文件、瓦片地图编辑器到处的地图格式tmx文件,实际上都是特定格式的xml文件

        另外 UserDefault 单例类保存的数据,也是存储在xml文件中的。

        Cocos2d-x 已经加入了 tinyxml2库 用于xml的解析。3.x版本位于external/tinyxml2下。

        本节要介绍的就是:如何使用 tinyxml2库 来操作处理xml文件。


    【参考】

        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)




    【XML简介】

        摘自:http://www.w3school.com.cn/xml/index.asp


    1、什么是XML?

        > XML 指可扩展标记语言(EXtensible Markup Language)。

        > XML 是一种标记语言,很类似 HTML。

        > XML 的设计宗旨是:传输数据,而非显示数据。

        > XML 标签没有被预定义,您需要自行定义标签。

        > XML 被设计为具有自我描述性。

        > XML 是 W3C 的推荐标准。


    2、XML的一个例子

        先来看一个简单的例子。

    //
    	<?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 文档中的元素形成了一棵文档树的结构。这棵树从“根部”开始,并扩展到树的“枝叶”。

    //
    	<root>
    		<child1>
    			<subchild1>.....</subchild1>
    			<subchild2>.....</subchild2>
    			.....
    		</child1>
    		<child2>
    			<subchild1>.....</subchild1>
    			<subchild2>.....</subchild2>
    			.....
    		</child2>
    		.....
    	</root>
    //

        如下所示,一个树结构的实例:

    wKioL1ThrrvCy2UXAADTKMjl7cM977.jpg

        它表示了XML中的一本书:

            > 根元素是 <bookstore>。文档中的所有 <book> 元素都被包含在 <bookstore> 中 。

            > 并且<book> 元素也有 4 个子元素:<title>、< author>、<year>、<price> 。

            > category、lang均为元素的属性。

            > <book>的4个子元素。

    //
    	<bookstore>
    		<book category="COOKING">
    			<title lang="en">Everyday Italian</title> 
    			<author>Giada De Laurentiis</author> 
    			<year>2005</year> 
    			<price>30.00</price> 
    		</book>
    		<book category="CHILDREN">
    			<title lang="en">Harry Potter</title> 
    			<author>J K. Rowling</author> 
    			<year>2005</year> 
    			<price>29.99</price> 
    		</book>
    		<book category="WEB">
    			<title lang="en">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 元素必须被正确的嵌套

    //
    	<!-- 错误的嵌套 -->
    	<b><i>This text is bold and italic</b></i>
    
    	<!-- 正确的嵌套 -->
    	<b><i>This text is bold and italic</i></b>
    //

        (5)XML 属性值必须加引号。(单引号'  '、双引号"  " ,均可以

    //
    	<note date="08/08/2008">
    	<note date='08/08/2008'>
    //


    4.1、注释

        在 XML 中编写注释的语法与 HTML 的语法很相似:

    //
    	<!-- This is a comment -->
    //


    4.2、保留空格

        HTML会把多个连续的空格字符裁减(合并)为一个。

        而在XML中,空格不会被删节。

    //
    	content :Hello    my name is David.
    	HTML    :Hello my name is David.
    	XML     :Hello    my name is David.
    //


    4.3、实体引用

        在 XML 中,一些字符拥有特殊的意义。

        如果你把字符 "<" 放在 XML 元素中,会发生错误,这是因为解析器会把它当作新元素的开始。

        这样会产生 XML 错误:

                <message>if salary < 1000 then</message>

        为了避免这个错误,请用实体引用来代替 "<" 字符:

                <message>if salary < 1000 then</message> 

        在 XML 中,有 5 个预定义的实体引用:

    <<小于
    >>大于
    &&
    和号
    &apos;'
    单引号
    ""
    双引号


    4.4、XML元素

        XML 元素指的是从(且包括)开始标签,直到(且包括)结束标签的部分。

        > 元素可包含:其他元素、文本、或者两者的混合物。

        > 元素也可以拥有属性。

    //
    	<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:属性的值必须加引号(单引号、双引号,均可以)。

    //
    	<file type="gif">computer.gif</file>
    //


    4.6、元素 vs. 属性

        有时候属性和元素均可以提供相同的信息。

        如下所示:

    //
    	<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、添加头文件

    //
    	#include "tinyxml2/tinyxml2.h"
    	using namespace tinyxml2;
    //


    2、XML数据解析

        XML文档如下:

    //
    	<?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解析使用举例:

    //
    //[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;
    //

        控制台输出结果:

    wKioL1Th_FeCLYSQAABFZ3mXVwI289.jpg


    3、XML数据存储

        以上面解析的XML文档为例,我们通过代码来生成相应的XML文档,并保存到xml文件中。

        生成XML文档并保存,举例:

    //
    //[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文档如下:

    wKioL1TiBU2zxfGcAADPDd8XuuQ334.jpg


    4、XML数据修改

        以上面存储的XML文档为例,进行数据的修改操作。

        原始XML文档数据如下:

    wKioL1TiBU2zxfGcAADPDd8XuuQ334.jpg

        XML文档数据修改,举例:

    //
    //[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文档如下:

    wKiom1TiCRKzI94aAAC_hEPMSVU422.jpg




    【常用数据操作】

        这里介绍一下常用的4个类的使用方法。

        > XMLNode          :表示一个节点,包含一般方法,如访问自节点、兄弟节点、编辑自身、编辑子节点。

        > XMLDocument :表示整个XML文档,不对应其中某个特定的节点。

        > XMLElement      :表示元素节点,可以包含子节点XMLElement、和属性XMLAttribute。 

        > XMLAttribute     :表示一个元素的属性。


    1、XMLNode

        表示一个节点,包含一般方法,如访问自节点、兄弟节点、编辑自身、编辑子节点。

        PS:它是 XMLDocument、XMLElement、XMLAttribute 的父类。

        常用方法如下:

    //
    // 获得该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,拥有父类所有的方法,这里不再赘述。

    //
    // 解析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,拥有父类所有的方法,这里不再赘述。

    //
    // 获取/设置 元素名称,等价于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

        表示一个元素的属性。

        没有父类。

        常用方法如下:

    //
    // 获取属性名称
    	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);
    //



关键字