XML格式使用越来越广泛,不论是游戏还是普通应用软件都有使用。而我喜欢用xml作为配置文件。XML解析的方法实在很多,最根本的两种是SAX和DOM。SAX需要一边读取文件一边解析,速度较快;而DOM采用树状结构保存解析文件,使用方便。iPone自带的XML解析器一则无法跨平台,一则使用不便,还有一些bug(我是指SDK 2.0,新版本SDK 3.0的XML解析器没用过,所以没有发言权),所以我决定使用其他开源XML解析器。开源的XML解析器也有很多种,比如libxml2,TouchXML(iPhone版),tinyxml等。其中tinyxml简单易用,如果你不要求诸如DTD等功能,那么tinyxml绝对应该是你的首选。我采用的是ticpp(tinyxml的cpp版本,采用DOM方法)。

 

项目下载

 

下面是ticpp的Xcode项目下载

 

(注:虽然我讨论的是跨平台代码,但我这里只是提供了Xcode项目,要在Windows下使用也是十分简单的事情,你可以自创Visual Studio项目文件或使用GNU make)

 

使用方法

 

这里我给出一个xml文件示例:

1
2
3
4
5
6
<options version="1.0">
<levels>
    <level name="InTheBeginning" enemy="10" />
    <level name="Fighting" enemy="100" />    
</levels>
</options>

 

首先,我定义了一个XML解析器类,它定义了XML解析的接口,我使用ticpp,但是如果你更改XML解析器为比如libxml2,你只需更改类的实现部分,你运用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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//////////////////////// 头文件 ///////////////////////////////////
/*
 *  xmlparser.h
 *  utils
 *
 *  Created by Bagusflyer on 09-6-27.
 *  Copyright 2009 www.iphone-geek.cn. All rights reserved.
 *
 */


#ifndef _UTILS_XMLPARSER_H_
#define _UTILS_XMLPARSER_H_

#include <fstream>
#include "ticpp/ticpp.h"
   
typedef ticpp::Element XmlNode;
typedef ticpp::Document XmlDocument;

using namspace std;

namespace utils
{

    class CXmlParser
    {
    public:
        CXmlParser():m_pRootNode(NULL) {}
       
        void parse(const string& file,const string& rootName);
        void parse(ifstream& stream,const string& rootName);
        void parse(const char* buf, size_t len, const string& rootNode);

        int getAttribute(XmlNode& elem, const string& attrib, int defval);
        float getAttributeFloat(XmlNode& elem, const string& attrib, float defval);
        string getAttribute(XmlNode& elem, const string& attrib);
       
        string getText(XmlNode& elem);
        string getName(XmlNode& elem);
       
        XmlNode* getFirstChild(XmlNode& elem,const string& nodeName);
        XmlNode* getFirstChild(XmlNode& elem);
        XmlNode* getNextChild(XmlNode& elem,string nodeName="");
        XmlNode* rootNode() const  
        {
            return m_pRootNode;
        }
       
    private:
        XmlDocument m_doc;
        XmlNode*    m_pRootNode;
    };
   
}

#endif // _UTILS_XMLPARSER_H_

//////////////////////////// 实现文件 ///////////////////////////////
/*
 *  xmlparser.cpp
 *  utils
 *
 *  Created by Bagusflyer on 09-6-27.
 *  Copyright 2009 www.iphone-geek.cn. All rights reserved.
 *
 */


#include "utils/xmlparser.h"

namespace utils
{

    int CXmlParser::getAttribute(XmlNode& elem,const string& attrib,int defval)
    {
    long value;
       
    elem.GetAttributeOrDefault(attrib,&value,defval);
        return value;
    }
   
    float CXmlParser::getAttributeFloat(XmlNode& elem, const string& attrib, float defval)
    {
        float value;
       
        elem.GetAttributeOrDefault(attrib,&value,defval);
                return value;      
    }
   
    string CXmlParser::getAttribute(XmlNode& elem,const string& attrib)
    {
        string value;

    elem.GetAttributeOrDefault(attrib,&value,_T(""));
       
        return value;
    }
   
    void CXmlParser::parse(const string& file,const string& rootName)
    {
        std::ifstream in(file.c_str());
        if ( !in.good() )
               {
                        // 注意:错误检查,略去
            //throw XmlParserError(_T("Can't open "+file));
        }
        parse(in,rootName);
    }
   
    void CXmlParser::parse(ifstream& stream,const string& rootName)
    {
        stream.seekg(0,std::ios::end);
        size_t len = stream.tellg();
        stream.seekg(0,std::ios::beg);
       
        char* buf = new char[len];
        stream.read(buf,len);
        try {
            parse((const char*)buf,len,rootName);
            delete [] buf;
        }
        catch (ticpp::Exception& ex) {
            delete [] buf;
            throw ex;
        }
    }
   
    void CXmlParser::parse(const char* buf, size_t len, const string& rootNode)
    {
        try
        {
            // Load a document
            std::string data(buf,len);
            m_doc.Parse(data);
           
            m_pRootNode = m_doc.FirstChildElement(rootNode);
        }
        catch( ticpp::Exception& ex )
        {
            // If any function has an error, execution will enter here.
            // Report the error
                        // 注意:请使用自己的XmlParser exception类
            // throw XmlParserError("Failed to parse xml");
        }
    }
   
    string CXmlParser::getText(XmlNode& elem)
    {
        return elem.GetTextOrDefault(_T(""));
    }
   
    string CXmlParser::getName(XmlNode& elem)
    {
        return elem.Value();
    }
   
    XmlNode* CXmlParser::getFirstChild(XmlNode& elem,const string& nodeName)
    {
        return elem.FirstChildElement(nodeName,false);
    }
   
    XmlNode* CXmlParser::getFirstChild(XmlNode& elem)
    {
        return elem.FirstChildElement(false);
    }
   
    XmlNode* CXmlParser::getNextChild(XmlNode& elem,string nodeName)
    {
        if ( nodeName.empty() )
            return elem.NextSiblingElement(false);
        else
            return elem.NextSiblingElement(nodeName,false);
    }
   
} // namespace utils

 

然后,在你的代码(比如我有一个COptionParser,它包含了一个utils::CXmlParser对象m_xmlPaser)中使用XmlParser进行解析:

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
/////////////////// 以下是进行解析的代码 //////////////////////////////
void parser(string& xmlFile)
{
    m_xmlParser.parse(xmlFile,"options");
    // 解析version属性
    string version = m_xmlParser.getAttribute(*(m_xmlParser.rootNode()),"version");

    XmlNode* pLevelsNode = m_xmlParser.getFirstChild(*m_xmlParser.rootNode(),"levels");
    if ( !pLevelsNode )
    {
        // 定义自己的exception
    //throw utils::XmlParserError("No levels found");
       
    if (pLevelsNode )
    {
            // 解析每个Level
            XmlNode* chd = m_xmlParser.getFirstChild(*pLevelsNode ,"level");
            while (chd)
            {
                // 解析level名称
                string name = m_xmlParser.getAttribute(*chd,"name");
                // 解析敌人数目,缺省值为15
                int enemies = m_xmlParser.getAttribute(elem,"enemy",15);
           
                // 下一个level
                chd = m_xmlParser.getNextChild(*chd,"level");
            }
    }
    }
}

 

代码虽然不少,可是思路还是很简单的。下一次,我会介绍跨平台压缩库。有什么问题,欢迎留言探讨!