C++ XML 解析器:tinyxml

C++ XML 解析器:tinyxml

1) TinyXML-2

一個簡單,輕量,高效的C++ XML 解析器,可以很容易得整合到其餘程序。javascript

TinyXML-2相比1,內存佔用更少,讀取更快,能更好得適應移動設備(Android)。html

2) 準備

2.1) 源碼

TinyXML-2源碼放在了GitHub上,其爲zlib license開源協議。java

當前最新release版爲:tinyxml2-2.1.0.tar.gznode

2.2) 編譯

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

3) 解析

3.1) 解析方式

TinyXML採用的是DOM方式,須要將XML整個加載到內存,將其看做樹狀結構進行操做。api

其餘方式還有:函數

  1. SAX:事件驅動,順序解析XML,遇到節點內容等時回調函數。相比DOM,佔用更少內存。
  2. Android上還提供了Pull:解析相似SAX,觸發事件時是個標識符,可用switch區分事件。
  3. 更重量級的有XQuery:直接語法查詢,能應用在任何類XML數據上,包括數據庫。

總之,雖然TinyXML-2想更好得適應移動設備,但DOM方式自己就不適合呢。Android上推薦Pull方式,但應該只有Java API吧。

3.2) 讀寫XML

Step 1: 生成XML樹,並寫入文件,基本步驟以下:

  1. XMLDocument::New*(): 生成各種節點
  2. XMLElement::SetAttribute(): 設置節點屬性
  3. XMLNode::Insert*(): 插入生成的節點
  4. XMLDocument::DeleteNode(),XMLNode::DeleteChild(): 刪除節點
  5. XMLDocument::SaveFile(): 保存爲文件

例如以下代碼:

/*
<?xml version="1.0" encoding="UTF-8"?>
<element>
  <!--comment-->
  <sub attr="1" />
  <sub attr="3" >&amp; 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: 從文件載入,並打印輸出:

  1. XMLDocument::LoadFile(): 從文件載入
  2. 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:

<?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()訪問鄰居,以遍歷同級節點。

  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"。

4) 總結

本文主要介紹了TinyXML-2的使用。其實,DOM操做的API都很相似的。

例如操做HTML DOM:

  1. 原生API的話,見document。能夠看到相似的createElement(),getElement*()等。不過查找節點,用querySelector()選擇器更方便。

  2. jQuery的話,相應API在分類ManipulationTraversing下。但找一些特定位置的子節點,直接選擇器Child Filter更簡單。


附1:樣例工程tinyxml_start

下載: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下是靜態庫。

相關文章
相關標籤/搜索