文档章节

C++ XML 解析器:tinyxml

加壹
 加壹
发布于 2014/04/24 16:32
字数 1279
阅读 2131
收藏 13

C++ XML 解析器:tinyxml

1) TinyXML-2

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

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

2) 准备

2.1) 源码

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

当前最新release版为:tinyxml2-2.1.0.tar.gz

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整个加载到内存,将其看作树状结构进行操作。

其他方式还有:

  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下是静态库。

© 著作权归作者所有

加壹
粉丝 25
博文 21
码字总数 20538
作品 0
无锡
私信 提问
TinyXML:一个优秀的C++ XML解析器

读取和设置xml配置文件是最常用的操作,试用了几个C++的XML解析器,个人感觉TinyXML是使用起来最舒服的,因为它的API接口和Java的十分类似,面向对象性很好。 TinyXML是一个开源的解析XML的解...

zmldndx
2013/10/10
1K
0
C++ 使用TinyXML解析XML文件

读取和设置xml配置文件是最常用的操作,TinyXML是一个开源的解析XML的C++解析库,能够在Windows或Linux中编译。这个解析库的模型通过解析XML文件,然后在内存中生成DOM模型,从而让我们很方便...

LoSingSang
2018/07/17
90
0
tinyxml在MFC下使用问题

今天在MFC下使用tinyXML库出现了一些问题,差了一下,有些人遇到了和我差不多的问题, 主要是库的冲突,现在还不知道怎么弄,我想在MFC使用tinyXML去解析界面控件的属性现在估计 不能使用了,...

ryany
2010/12/20
946
0
klish 1.6.0 发布,仿 CISCO 的命令行接口

klish 1.6.0 发布,该版本将原有的一些 C++ 代码全部用 C 重写,因此编译器无需支持 C++ 就可以编译;移除了 tinyXML 内部实现;支持使用外部的 XML 解析器,包括:libxml2, expat, and lib...

oschina
2012/07/03
843
0
用tinyxml写的程序运行时错误

因为是新手刚学习tinyxml,所以谢了个生成xml文件的cpp试试,编译时没问题,但是程 序运行时会出现: xml: malloc.c:2395: sysmalloc: Assertion `(old_top == initial_top (av) && old_size...

黑水夜夜夜夜
2016/04/28
400
1

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
今天
319
5
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
11
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部