当前位置: 首页 > 工具软件 > tinyscript > 使用案例 >

c++ android xml解析框架,C++ XML 解析器:tinyxml

杜英叡
2023-12-01

C++ XML 解析器:tinyxml

一个简单,轻量,高效的C++ XML 解析器,能够很容易得整合到其他程序。

TinyXML-2相比1,内存占用更少,读取更快,能更好得适应移动设备(Android)。

2) 准备

2.1) 源码

TinyXML-2源码放在了GitHub上,其为zlib license开源协议。

2.2) 编译

tinyxml2-2.1.0/tinyxml2/目录下是工程文件。居然有C::B的cbp,其配置的GCC Compiler,Window上用MinGW即可。

TinyXML-2仅有三个文件:tinyxml2.h,tinyxml2.cpp是其核心代码;xmltest.cpp是其测试代码。

需要注意tinyxml2.h内的宏定义:

ANDROID_NDK # for Android

_WIN32 # for Win32

TINYXML2_EXPORT # 动态库导出

TINYXML2_IMPORT # 动态库导入

_DEBUG | DEBUG # debug

自行配置的话,若在Windows上生成dll,注意定义宏_WIN32,TINYXML2_EXPORT。链接其的工程最好加个宏TINYXML2_IMPORT,减去不必要的寻址。其对应内容如下:

#ifdef _WIN32

# ifdef TINYXML2_EXPORT

# define TINYXML2_LIB __declspec(dllexport)

# elif defined(TINYXML2_IMPORT)

# define TINYXML2_LIB __declspec(dllimport)

# else

# define TINYXML2_LIB

# endif

#else

# define TINYXML2_LIB

#endif

3) 解析

3.1) 解析方式

TinyXML采用的是DOM方式,需要将XML整个加载到内存,将其看作树状结构进行操作。

其他方式还有:

SAX:事件驱动,顺序解析XML,遇到节点内容等时回调函数。相比DOM,占用更少内存。

Android上还提供了Pull:解析类似SAX,触发事件时是个标识符,可用switch区分事件。

更重量级的有XQuery:直接语法查询,能应用在任何类XML数据上,包括数据库。

总之,虽然TinyXML-2想更好得适应移动设备,但DOM方式本身就不适合呢。Android上推荐Pull方式,但应该只有Java API吧。

3.2) 读写XML

Step 1: 生成XML树,并写入文件,基本步骤如下:

XMLDocument::New*(): 生成各类节点

XMLElement::SetAttribute(): 设置节点属性

XMLNode::Insert*(): 插入生成的节点

XMLDocument::DeleteNode(),XMLNode::DeleteChild(): 删除节点

XMLDocument::SaveFile(): 保存为文件

例如如下代码:

/*

& Text!

*/

bool CreateXml(const char *filename) {

XMLDocument *doc = new XMLDocument();

// use the standard declaration by default

XMLDeclaration *declaration = doc->NewDeclaration();

doc->InsertFirstChild(declaration);

// insert 'element' node

XMLNode *element = doc->InsertEndChild(doc->NewElement("element"));

// insert 'sub' nodes

XMLElement *subs[3];

for (int i = 0; i < 3; ++i) {

XMLElement *sub = doc->NewElement("sub");

sub->SetAttribute("attr", i+1);

element->InsertEndChild(sub);

subs[i] = sub;

}

// insert text to 3rd 'sub' node

XMLText *text = doc->NewText("& Text!");

// text->SetCData(true); //

subs[2]->InsertFirstChild(text);

// delete 2nd 'sub' node

element->DeleteChild(subs[1]);

// doc->DeleteNode(subs[1]);

// insert 'comment' node

element->InsertFirstChild(doc->NewComment("comment"));

// save xml, true for compact

XMLError error = doc->SaveFile(filename, true);

delete doc;

return error == 0;

}

Step 2: 从文件载入,并打印输出:

XMLDocument::LoadFile(): 从文件载入

XMLDocument::Print(): 直接打印,或用XMLPrinter转为字符串

这样即可:

bool PrintXml(const char *filename) {

XMLDocument doc;

if (!doc.LoadFile(filename)) {

// doc.Print();

XMLPrinter streamer(0, false); // true for compact

doc.Print(&streamer);

printf("%s", streamer.CStr());

}

return false;

}

以上代码,具体请见附1"src/xmlhandle.cc"。

3.2) 解析XML

例如此段XML:

0

(function(x, y) {

if (x < y && x > 0) {

return true;

} else {

return false;

}

}(x, y));

]]>

Step 1: 直接对字符串进行解析:

static const char *xml =

"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"

// ...

"";

XMLDocument doc;

doc.Parse(xml);

// doc.Print();

if (doc.ErrorID() != 0) {

doc.PrintError();

} else {

// 对doc操作解析

}

Step 2: 准备简单存储结构,以存放解析结果:

namespace script {

struct Value {

Type type;

std::string value;

};

struct Bundle {

std::string scope;

std::map<:string value> values;

};

struct Content {

std::string lang;

std::string text;

};

struct Script {

int key;

Bundle bundle;

Content content;

};

} // script

Step 3: 用XMLNode::NextSiblingElement()访问邻居,以遍历同级节点。

array下同级script:

// root 'array' node XMLElement *el_root = doc.FirstChildElement("array"); if (el_root) { // ... // traverse 'script' node XMLElement *el_script = el_root->FirstChildElement("script"); while (el_script) { // ... // next 'script' node el_script = el_script->NextSiblingElement("script"); } // ... }

不确定名称时,如bundle下可能x,y,z或其他:

XMLElement *el_bundle = el_script->FirstChildElement("bundle"); if (el_bundle) { // ... // traverse 'bundle' child values XMLElement *el_value = el_bundle->FirstChildElement(); while (el_value) { const char *name = el_value->Name(); // won't Null // ... el_value = el_value->NextSiblingElement(); } }

Step 4: 用XMLElement::Attribute()获取节点属性,GetText()获取文本。

获取content的lang属性及其内容:

XMLElement *el_content = el_script->FirstChildElement("content"); if (el_content) { // 'content' 'lang' attribute const char *lang = el_content->Attribute("lang"); if (lang) { script.content.lang = std::string(lang); } // 'content' text const char *text = el_content->GetText(); if (text) { script.content.text = std::string(text); } }

Step 5: 用类似XMLElement::QueryIntAttribute()、QueryIntText(),直接转换类型。

直接将key文本存为int:

XMLElement *el_key = el_script->FirstChildElement("key"); if (!el_key || el_key->QueryIntText(&script.key) != 0) { // none exists, XML_NO_TEXT_NODE, XML_CAN_NOT_CONVERT_TEXT }

Step 6: 获取注释试试看:

// the comment

XMLNode *nd_comment = el_root->FirstChild();

if (nd_comment && nd_comment->ToComment()) {

std::stringstream comment;

comment << "";

std::cout << comment.str() << std::endl;

}

以上代码,具体请见附1"src/script.h与xmlparse.cc"。

4) 总结

本文主要介绍了TinyXML-2的使用。其实,DOM操作的API都很类似的。

例如操作HTML DOM:

原生API的话,见document。可以看到类似的createElement(),getElement*()等。不过查找节点,用querySelector()选择器更方便。

jQuery的话,相应API在分类Manipulation、Traversing下。但找一些特定位置的子节点,直接选择器Child Filter更简单。

附1:样例工程tinyxml_start

目录树如下:

tinyxml_start/

├─build/

│ ├─tinyxml_start-gcc.cbp # for gnu gcc

│ └─tinyxml_start-msvc.cbp # for msvc 2010

├─src/

├─third_party/

│ └─tinyxml2-2.1.0/

└─tools/

tinyxml2下载解压到third_party/目录下,用C::B打开cbp工程文件即可。

tinyxml2在Windows上配置生成的是动态库,Linux下是静态库。

 类似资料: