转载自【黑米GameDev街区】 原文链接: http://www.himigame.com/iphone-cocos2dx/694.html
本章Himi给大家分享如何在Cocos2dX中解析xml数据;对于数据存取有很多方式,流文件,plist,xml等,那么为了跨平台更好的支持,Himi想到之前写的CCUserDefault 存储数据一节,Cocos2dx自带的存储类,一旦存入数据都会以xml格式进行保存,适用于iOS、Android等平台,所以这里Himi使用xml进行游戏的一些数据录入 = =.. 另外一方面Himi本章节也是基于Cocos2dx引擎代码进行的一次简单对xml数据解析的封装;
为了更保险的去考虑跨平台,所以对于xml存储这块的解析,也做了些搜索,最后发现王哥(王哲-cocos2dx引擎作者)也有给我们提示过,内容如下:
1
2
3
|
cocos2dx里面集成了
libxml2,
ios上会调用
sdk里面内置的,
android和
win32上则带了已经编译好的静态
/动态库。
你可以参考
CCSAXParser里面的代码来使用
libxml2
|
那么既然如此,就对Cocos2dx引擎源码的CCSAXParser类进行了剖析,那么这里Himi,先给出代码,然后再详细讲解下:
Himi封装的HXmlParse类:
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
|
HXmlParse
.
h
//
// HXmlParse.h
// HAnimation
//
// Created by Himi on 12-3-22.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#ifndef HAnimation_HXmlParse_h
#define HAnimation_HXmlParse_h
#include "cocos2d.h"
#include "CCSAXParser.h"
#include "CCObject.h"
#include "CCMutableDictionary.h"
using
namespace
cocos2d
;
class
CC_DLL
HXmlParse
:
public
CCObject
,
public
CCSAXDelegator
{
public
:
static
HXmlParse *
parserWithFile
(
const
char
*
tmxFile
)
;
bool
initHXmlParse
(
const
char
*
xmlName
)
;
// 使用 CCSAXDelegator 重写3个回调函数
void
startElement
(
void
*
ctx
,
const
char
*
name
,
const
char
*
*
atts
)
;
void
endElement
(
void
*
ctx
,
const
char
*
name
)
;
void
textHandler
(
void
*
ctx
,
const
char
*
ch
,
int
len
)
;
std
::
string
root_name
;
bool
isJumpHeadData
;
CCMutableDictionary
&
lt
;
std
::
string
,
CCString*
&
gt
;
*
mDic
;
private
:
std
::
string
startXmlElement
;
//用来记录每个key前字段
std
::
string
endXmlElement
;
//用来记录每个key后字段
std
::
string
currString
;
//记录每个value的值
}
;
#endif
|
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
|
HXmlParse
.
cpp
//
// HXmlParse.cpp
// HAnimation
//
// Created by Himi on 12-3-22.
// Copyright (c) 2012年 Augustimpression. All rights reserved.
//
#include "HXmlParse.h"
#include "CCSAXParser.h"
HXmlParse *
HXmlParse
::
parserWithFile
(
const
char
*
tmxFile
)
{
HXmlParse *
pRet
=
new
HXmlParse
(
)
;
if
(
pRet
-
&
gt
;
initHXmlParse
(
tmxFile
)
)
{
pRet
-
&
gt
;
autorelease
(
)
;
return
pRet
;
}
CC_SAFE_DELETE
(
pRet
)
;
return
NULL
;
}
bool
HXmlParse
::
initHXmlParse
(
const
char
*
xmlName
)
{
mDic
=
new
CCMutableDictionary
&
lt
;
std
::
string
,
CCString*
&
gt
;
(
)
;
CCSAXParser
_par
;
if
(
false
==
_par
.
init
(
"UTF-8"
)
)
{
CCLog
(
"-----请使用utf-8格式!"
)
;
return
false
;
}
_par
.
setDelegator
(
this
)
;
const
char
*
_path
=
CCFileUtils
::
fullPathFromRelativePath
(
xmlName
)
;
return
_par
.
parse
(
_path
)
;
}
//回调函数
void
HXmlParse
::
startElement
(
void
*
ctx
,
const
char
*
name
,
const
char
*
*
atts
)
{
CC_UNUSED_PARAM
(
ctx
)
;
startXmlElement
=
(
char
*
)
name
;
if
(
!
isJumpHeadData
)
{
//跳过数据头
CCLog
(
"------跳过root name"
)
;
isJumpHeadData
=
true
;
root_name
=
startXmlElement
;
return
;
}
// CCLog("-startElement----%s",startXmlElement.c_str());
}
void
HXmlParse
::
endElement
(
void
*
ctx
,
const
char
*
name
)
{
CC_UNUSED_PARAM
(
ctx
)
;
endXmlElement
=
(
char
*
)
name
;
if
(
endXmlElement
==
root_name
)
{
//数据尾
CCLog
(
"读取xml结束"
)
;
isJumpHeadData
=
false
;
root_name
=
""
;
return
;
}
// CCLog("-endElement----%s",endXmlElement.c_str());
}
//键值对的结束字段
void
HXmlParse
::
textHandler
(
void
*
ctx
,
const
char
*
ch
,
int
len
)
{
CC_UNUSED_PARAM
(
ctx
)
;
currString
=
string
(
(
char
*
)
ch
,
0
,
len
)
;
CCString *
ccStr
=
new
CCString
(
)
;
//备注3
ccStr
-
&
gt
;
m_sString
=
currString
;
if
(
root_name
!=
""
)
{
mDic
-
&
gt
;
setObject
(
ccStr
,
startXmlElement
)
;
CCLog
(
"-----key:%s, value:%s"
,
startXmlElement
.
c_str
(
)
,
mDic
-
&
gt
;
objectForKey
(
startXmlElement
)
-
&
gt
;
m_sString
.
c_str
(
)
)
;
}
// CCLog("-textHandler----%s",currString.c_str());
}
|
OK,代码呢我们先从.h中来说,首先我们使用CCSAXDelegator,为了让CCSAXParser解析数据后将数据回调给如下三个函数:
1
2
3
4
5
6
7
|
// 使用 CCSAXDelegator 重写3个回调函数
void
startElement
(
void
*
ctx
,
const
char
*
name
,
const
char
*
*
atts
)
;
void
endElement
(
void
*
ctx
,
const
char
*
name
)
;
void
textHandler
(
void
*
ctx
,
const
char
*
ch
,
int
len
)
;
|
startElement 函数解析的是xml的每个key前字段
textHandler 函数解析出来的是xml每个key对应的value值
endElement 函数解析出来的是xml的每个key后字段
这里Himi随便写了一个xml来做测试,himi.xml,如下:
1
|
&
lt
;
?
xml
version
=
"1.0"
encoding
=
"utf-8"
?
&
gt
;
&
lt
;
himiTestData
&
gt
;
&
lt
;
key1
&
gt
;
1000
&
lt
;
/
key1
&
gt
;
&
lt
;
key2
&
gt
;娃哈哈
&
lt
;
/
key2
&
gt
;
&
lt
;
key3
&
gt
;
82.3
&
lt
;
/
key3
&
gt
;
&
lt
;
key4
&
gt
;
4000
&
lt
;
/
key4
&
gt
;
&
lt
;
key5
&
gt
;
himi
&
lt
;
/
key5
&
gt
;
&
lt
;
key6
&
gt
;
true
&
lt
;
/
key6
&
gt
;
&
lt
;
/
himiTestData
&
gt
;
|
那么CCSAXParser类解析第一次回调 startElement 是读取的是root name(xml数据头标识名称->“<himiTestData>”),然后才读取正式数据key和value,最后读取的也是xml数据尾标识名称“</himiTestData>”
当然在Himi封装的HXmlParse类中对于数据标识的读取都跳过了,使用变量isJumpHeadData来处理的;
其他的都很容易没有什么可说的,主要要说还有一点就是关于CCMutableDictionary的使用,对于此类主要结构是形成map&NSMutableDictionary类似是个键值对容器,key-value;那么使用时候要注意4点:
1. 比如Himi解析数据后都会默认将key和value数据存放在CCMutableDictionary中,那么这里我肯定传入的是两个string,但是细心的童鞋会发现代码中第二个并不是std::string,而是CCString对象,嗯 没错,CCMutableDictionary要求传入的是CCObject对象而不是基本类型!CCString中有m_sString这个属性,所以转换起来也是很方便的;
2. 使用CCMutableDictionary进行添加数据setObject的时候要注意此函数的两个参数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
bool
setObject
(
_ValueT
pObject
,
const
_KeyT
&
amp
;
key
)
{
pair
&
lt
;
CCObjectMapIter
,
bool
&
gt
;
pr
;
pr
=
m_Map
.
insert
(
Int_Pair
(
key
,
pObject
)
)
;
if
(
pr
.
second
==
true
)
{
pObject
-
&
gt
;
retain
(
)
;
return
true
;
}
return
false
;
}
|
上面这个是cocos2dx引擎中源码中setObject函数实现代码,这里可以很清晰的看到,第一个参数表示《CCObject》,第二个参数才是《Key》!这点对于之前做过java开发的我来说比较郁闷,因为一般都是key在第一个参数。。。。。
3. 请大家仔细看HXmlParse.cpp类中的备注3 ,当你使用CCMutableDictionary的setObject函数的时候,务必要注意,此函数的存入的CCObject参数,引擎中实现代码是对你这个CCObject进行retain()的一个内存地址引用!也就是说这里不要使用一个成员变量来使用,否则你从CCMutableDictionary取出来的数据全部是你最后一个CCObject数据!
4. CCMutableDictionary是cocos2d-x自己封装的类,功能类似NSMutableDictionary。但是Himi通过测试发现!它有一点和NSMutableDictionary是不一样的。NSMutableDictionary的setObjectForKey方法是:如果发现这个key已经存在于字典中的时候,它会自动用新的object覆盖掉原有的object。而CCMutableDictionary由于它是使用map实现的字典功能,而在map里面,如果key已存在,是不会用新的object覆盖掉原有object的。在使用CCMutableDictionary的时候需要特别注意这一点。
HXmlParse解析xml类使用方法很简单:
1
2
3
|
#include "HXmlParse.h"
HXmlParse
::
parserWithFile
(
"himi.xml"
)
;
|
然后Himi为了证实此解析类在Android也可以正常运行,那么这里Himi将读出的数据展示在画面上,iOS运行截图如下: