添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

Libxml2是由C语言开发的一个库,除了解析XML文档外,还可以解析HTML文档(不过这我没有研究)。同时,libxml2库可移植于多个平台
大致可与运行在如下平台: Linux, Unix, Windows, CygWin, MacOS, MacOS X, RISC Os, OS/2, VMS, QNX, MVS, VxWorks

可以从官网: http://xmlsoft.org 获取最新版本(2.9.8)的libxml2库。Linux下编译也很简单
./configure
make install

官网上也有一些例子,以及API接口文档,常见问题,Tutorial,建议去看看。也可以从下载的源文件目录doc中离线浏览

libxml2库囊括了以下的模块,

Table of Contents

DOCBparser : old DocBook SGML parser
HTMLparser : interface for an HTML 4.0 non-verifying parser
HTMLtree : specific APIs to process HTML tree, especially serialization
SAX : Old SAX version 1 handler, deprecated
SAX2 : SAX2 parser interface used to build the DOM tree
c14n : Provide Canonical XML and Exclusive XML Canonicalization
catalog : interfaces to the Catalog handling system
chvalid : Unicode character range checking
debugXML : Tree debugging APIs
dict : string dictionary
encoding : interface for the encoding conversion functions
entities : interface for the XML entities handling
globals : interface for all global variables of the library
hash : Chained hash tables
list : lists interfaces
nanoftp : minimal FTP implementation
nanohttp : minimal HTTP implementation
parser : the core parser module
parserInternals : internals routines and limits exported by the parser.
pattern : pattern expression handling
relaxng : implementation of the Relax-NG validation
schemasInternals : internal interfaces for XML Schemas
schematron : XML Schemastron implementation
threads : interfaces for thread handling
tree : interfaces for tree manipulation
uri : library of generic URI related routines
valid : The DTD validation
xinclude : implementation of XInclude
xlink : unfinished XLink detection module
xmlIO : interface for the I/O interfaces used by the parser
xmlautomata : API to build regexp automata
xmlerror : error handling
xmlexports : macros for marking symbols as exportable/importable.
xmlmemory : interface for the memory allocator
xmlmodule : dynamic module loading
xmlreader : the XMLReader implementation
xmlregexp : regular expressions handling
xmlsave : the XML document serializer
xmlschemas : incomplete XML Schemas structure implementation
xmlschemastypes : implementation of XML Schema Datatypes
xmlstring : set of routines to process strings
xmlunicode : Unicode character APIs
xmlversion : compile-time version informations
xmlwriter : text writing API for XML
xpath : XML Path Language implementation
xpathInternals : internal interfaces for XML Path Language implementation
xpointer : API to handle XML Pointers

因为libxml2包含大量函数,实现有不同的方式。这里介绍一些常用的。至于其他的功能有兴趣自己研究吧……
而这里主要用到的为 parser,tree,xmlreader,xmlwriter 这几个模块

parser/tree

读取XML文档

首先介绍xml文档的解析吧,大致调用函数如下

  • xmlReadFile/xmlParseFile 打开一个XML文档并返回一个文档对象指针xmlDocPtr
  • xmlDocGetRootElement 获取XML文档的根节点xmlNodePtr
  • 获取根节点,以及childNode的名称、属性名/值。而这一步骤可以通过递归实现。
  • 最后由 xmlFreeDoc、xmlCleanupParser 释放所有分配的内存
  • 下面给出一个例子(C++)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    #include <iostream>
    #include <libxml/parser.h>
    #include <libxml/tree.h>
    using namespace std;
    ///
    /// \param depth 递归深度
    /// \param _xmlNodePtr 节点对象指针
    void ParserXML(int &depth,xmlNodePtr _xmlNodePtr){
    bool ishas_child=false;
    // 所有子节点
    xmlNodePtr xmlNodePtr1= _xmlNodePtr->children;
    int count=0;
    while (xmlNodePtr1){
    // 判断节点类型
    if(xmlNodePtr1->type!=XML_TEXT_NODE){
    // xmlStrcmp(xmlNodePtr1->name,BAD_CAST "text")
    count++;
    // 子节点个数
    int childEleCount= xmlChildElementCount(xmlNodePtr1);
    cout.width(depth);
    if(childEleCount==0){
    ishas_child=false;
    // 表明无子节点
    cout<<count<<"."<<xmlNodePtr1->name<<" --> "<<xmlNodeGetContent(xmlNodePtr1)<<endl;
    }else{
    ishas_child=true;
    // 表明有子节点
    cout<<count<<"."<<xmlNodePtr1->name<<endl;
    }
    // 遍历节点属性
    xmlAttr* xmlAttr1=xmlNodePtr1->properties;
    if(xmlAttr1){
    cout.width(depth+1);
    cout<<"=>";
    while (xmlAttr1!=NULL){
    // 判断使用存在属性 , xmlGetProp获取属性值
    if(xmlHasProp(xmlNodePtr1,xmlAttr1->name)){
    cout<<" "<<xmlAttr1->name<<":"<<
    xmlGetProp(xmlNodePtr1,xmlAttr1->name);
    }
    xmlAttr1=xmlAttr1->next;
    }
    cout<<endl;
    }
    // 递归调用
    if(ishas_child){
    depth+=10;
    ParserXML(depth,xmlNodePtr1);
    }
    }
    xmlNodePtr1=xmlNodePtr1->next;
    }
    if(depth>0){
    depth-=10;
    }
    }
    int main(int argc,char** argv) {
    if(argc!=2)
    return 0;
    xmlDocPtr xmlDocPtr1=NULL;
    //xmlDocPtr1=xmlParseFile(argv[1]);
    xmlInitParser();
    // 读取文件
    xmlDocPtr1=xmlReadFile(argv[1],"UTF-8",0);
    if(xmlDocPtr1==NULL) {
    xmlErrorPtr xmlErrorPtr1 =xmlGetLastError();
    cout << xmlErrorPtr1->message << endl;
    exit(-1);
    }
    cout<<"Version: "<<xmlDocPtr1->version<<endl;
    cout<<"Encoding: "<<xmlDocPtr1->encoding<<endl;
    // 获取根节点
    xmlNodePtr xmlNodePtr1= xmlDocGetRootElement(xmlDocPtr1);
    cout<<"Root Element: "<<xmlNodePtr1->name<<endl<<endl;
    // 遍历所有信息
    int depth=0;
    ParserXML(depth,xmlNodePtr1);
    // 释放内存
    xmlFreeDoc(xmlDocPtr1);
    xmlCleanupParser();
    return 0;
    }

    有如下 test.xml 文档

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    <?xml version="1.0" encoding="UTF-8"?>
    <People>
    <Student id="0x00000000">
    <name>Jack</name>
    <age>14</age>
    <sex>Man</sex>
    <hobby>Computer Programming</hobby>
    </Student>
    <Student id="0x11111111">
    <name>Tony</name>
    <age>17</age>
    <sex>Man</sex>
    <hobby>Play Basketball</hobby>
    </Student>
    <Student id="0x22222222">
    <name>Job</name>
    <age>20</age>
    <sex>Man</sex>
    <hobby>Play Football</hobby>
    </Student>
    </People>

    编译运行 g++ main.cpp -o main xml2-config –libs –cflags && ./main test.xml

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    Version: 1.0
    Encoding: UTF-8
    ROOT Element: People
    1.Student
    => id:0x00000000
    1.name --> Jack
    2.age --> 14
    3.sex --> Man
    4.hobby --> Computer Programming
    2.Student
    => id:0x11111111
    1.name --> Tony
    2.age --> 17
    3.sex --> Man
    4.hobby --> Play Basketball
    3.Student
    => id:0x22222222
    1.name --> Job
    2.age --> 20
    3.sex --> Man
    4.hobby --> Play Football

    可以看到,成功的解析xml文档并格式化的输出信息。这个例子的核心代码就是那个递归函数。现在我们来看看它到底干了些什么。
    不过在此之前,先谈谈 xmlParseFile xmlReadFile 的区别。它们都是打开一个XML文档并返回一个文档对象指针 xmlDocPtr ,不同之处就在于它们提供的参数不同

    1
    2
    xmlDocPtr xmlParseFile(const char *filename);
    xmlDocPtr xmlReadFile(const char *URL,**const char *encoding**,int options);

    xmlReadFile 以指定的编码格式打开xml文档,而 xmlParseFile 默认以UTF-8编码格式打开文档。
    比如,刚才的test.xml 中通过encoding指定了UTF-8编码格式,那么用 xmlParseFile 能成功解析,用 xmlReadFile 函数无论
    const char *encoding 参数为NULL还是“UTF-8” 也能成功解析。
    但是 test.xml 只有 那么 xmlParseFile 会解析失败,而 xmlReadFile 只有 第二个参数为 “UTF-8”时才能解析成功。

    好了,继续看那个递归函数吧。。。
    首先用 xmlNodePtr xmlNodePtr1= _xmlNodePtr->children; 获取的 _xmlNodePtr 节点的所有子节点,然后在while循环中判断每个子节点的类型, xmlNodePtr1->type!=XML_TEXT_NODE 如果不是 XML_TEXT_NODE 那就继续。接着 用 xmlChildElementCount 获取 xmlNodePtr1 节点指针的所有子节点个数,并用一个 ishas_child 标识是否存在子节点,对于存在子节点的节点,就进行 递归 。然后显示了节点的属性,通过 xmlHasProp 判断是否存在指定名字的属性,存在用 xmlGetProp 获取其值。注意,我获取了 一个属性对象(链表) xmlAttr* xmlAttr1=xmlNodePtr1->properties 之后进行遍历。在libxml2中,xmlNode、xmlDoc、xmlAttr 都是一个链表。
    之后进入递归,depth 只是表示深度,用于格式化输出
    if(ishas_child){
    depth+=10;
    ParserXML(depth,xmlNodePtr1);
    在进入的每个递归函数执行结束之前减去刚才进入的深度
    if(depth>0){
    depth-=10;

    接下来就是生成XML文档。。。

    生成XML文档

    生成就简单一些了,下面是一个例子

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #include <iostream>
    #include <libxml/parser.h>
    #include <libxml/tree.h>
    using namespace std;
    void GenerateXML(const char *filename){
    xmlDocPtr xmlDocPtr1= xmlNewDoc(BAD_CAST XML_DEFAULT_VERSION);
    if(!xmlDocPtr1){
    cout<<"ERROR: "<<xmlGetLastError()->message<<endl;
    }
    // 创建根节点
    xmlNodePtr xmlRootPtr=xmlNewNode(NULL,BAD_CAST"Root");
    xmlDocSetRootElement(xmlDocPtr1,xmlRootPtr);
    // 创建一个文本子节点,父节点为 xmlRootPtr
    xmlNodePtr xmlNodePtr1= xmlNewTextChild(xmlRootPtr,NULL,BAD_CAST"Management",BAD_CAST"None");
    // 添加一个节点,父节点为 xmlNodePtr1
    xmlNewTextChild(xmlNodePtr1,NULL,BAD_CAST"Score",BAD_CAST"None");

    // 创建一个新的节点
    xmlNodePtr xmlAnimalPtr= xmlNewNode(NULL,BAD_CAST"Animals");
    xmlAddChild(xmlRootPtr,xmlAnimalPtr);
    // 创建元素
    xmlAttrPtr xmlAttrPtr1= xmlNewProp(xmlAnimalPtr,BAD_CAST"Number",BAD_CAST"888");
    xmlSetProp(xmlAnimalPtr,BAD_CAST"Number",BAD_CAST"9999");
    // xmlNodeSetName(xmlAnimalPtr,BAD_CAST"The_Animal");
    // xmlRemoveProp(xmlAttrPtr1);
    // 保存文件
    if(xmlSaveFileEnc(filename,xmlDocPtr1,"UTF-8")){
    cout<<"生成 XML 文件成功!"<<endl;
    }
    // 释放内存
    xmlFreeDoc(xmlDocPtr1);
    }
    int main()
    {
    GenerateXML("test.xml");
    }

    这里通用的做法为 xmlNewDoc 创建一个新的XML文档并返回一个 xmlDocPtr ,创建一个根节点就是创建一个 xmlNodePtr 并通过 xmlDocSetRootElement 设置 xmlDocPtr 的根节点为 xmlNodePtr 。之后在继续添加子节点时,只需在 xmlNodePtr 的基础上创建新节点并添加即可。

    xmlNewTextChild 函数用于文件一个文本子节点。如 <node>Hello</node>

    创建新节点的方法为 xmlNewNode 它返回一个 xmlNodePtr ,之后 xmlAddChild 把一个节点添加到父节点上。

    通过 xmlNewProp 创建一个属性 xmlAttrPtr ,之后可通过
    xmlSetProp或xmlNodeSetName 设置属性的值。删除属性 xmlRemoveProp

    最后,全部OK了, xmlSaveFileEnc 来保存XML文档

    编译运行生成test.xml,可能格式有点混乱,我修改了一下

    1
    2
    3
    4
    5
    6
    7
    <?xml version="1.0" encoding="UTF-8"?>
    <Root>
    <Management>None
    <Score>None</Score>
    </Management>
    <Animals Number="9999"/>
    </Root>

    OK,基本上解析XML也不算太难。除了libxml2库外,还有其他的库也可以解析XML。看个人爱好了 😃