一个简单,轻量,高效的C++ XML 解析器,能够很容易得整合到其他程序。
TinyXML-2相比1,内存占用更少,读取更快,能更好得适应移动设备(Android)。
TinyXML-2源码放在了GitHub上,其为zlib license开源协议。
当前最新release版为:tinyxml2-2.1.0.tar.gz。
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
TinyXML采用的是DOM方式,需要将XML整个加载到内存,将其看作树状结构进行操作。
其他方式还有:
总之,虽然TinyXML-2想更好得适应移动设备,但DOM方式本身就不适合呢。Android上推荐Pull方式,但应该只有Java API吧。
Step 1: 生成XML树,并写入文件,基本步骤如下:
例如如下代码:
/*
<?xml version="1.0" encoding="UTF-8"?>
<element>
<!--comment-->
<sub attr="1" />
<sub attr="3" >& Text!</sub>
</element>
*/
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); // <![CDATA[& Text!]]>
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: 从文件载入,并打印输出:
这样即可:
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”。
例如此段XML:
<content lang="javascript">
<array>
<!-- a script -->
<script>
<key>0</key>
<bundle scope="global">
<x type="n" value="2"/>
<y type="n" value="7"/>
<z/>
</bundle>
<content lang="javascript">
<![CDATA[
(function(x, y) {
if (x < y && x > 0) {
return true;
} else {
return false;
}
}(x, y));
]]>
</content>
</script>
</array>
Step 1: 直接对字符串进行解析:
static const char *xml =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
// ...
"</array>";
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<std::string, Value> values;
};
struct Content {
std::string lang;
std::string text;
};
struct Script {
int key;
Bundle bundle;
Content content;
};
} // script
Step 3: 用XMLNode::NextSiblingElement()
访问邻居,以遍历同级节点。
1) 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");
}
// ...
}
2) 不确定名称时,如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()
获取文本。
1) 获取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()
,直接转换类型。
1) 直接将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 << "<!--" << nd_comment->Value() << "-->";
std::cout << comment.str() << std::endl;
}
以上代码,具体请见附1"src/script.h与xmlparse.cc”。
本文主要介绍了TinyXML-2的使用。其实,DOM操作的API都很类似的。
例如操作HTML DOM:
原生API的话,见document。可以看到类似的createElement(),getElement*()等。不过查找节点,用querySelector()选择器更方便。
jQuery的话,相应API在分类Manipulation、Traversing下。但找一些特定位置的子节点,直接选择器Child Filter更简单。
下载:tinyxml_start.zip。
目录树如下:
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下是静态库。