一個簡單,輕量,高效的C++ XML 解析器,可以很容易得整合到其餘程序。javascript
TinyXML-2相比1,內存佔用更少,讀取更快,能更好得適應移動設備(Android)。html
TinyXML-2源碼放在了GitHub上,其爲zlib license開源協議。java
當前最新release版爲:tinyxml2-2.1.0.tar.gz。node
tinyxml2-2.1.0/tinyxml2/目錄下是工程文件。竟然有C::B的cbp,其配置的GCC Compiler,Window上用MinGW便可。jquery
TinyXML-2僅有三個文件:tinyxml2.h,tinyxml2.cpp是其核心代碼;xmltest.cpp是其測試代碼。git
須要注意tinyxml2.h內的宏定義:github
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整個加載到內存,將其看做樹狀結構進行操做。api
其餘方式還有:函數
總之,雖然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:
<?xml version="1.0" encoding="UTF-8"?> <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()
訪問鄰居,以遍歷同級節點。
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 << "<!--" << 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/ ├─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下是靜態庫。