1、ExpatExpat 是 什 么 ?Expat 是 一 个 用 C 语 言 开 发 的 、 用 来 解 析 XML 文 档 的 开 发 库 , 它 最 初 是 开 源 的 、Mozilla 项 目 下 的 一 个 XML 解 析 器 。 关 于 作 者这 个 库 的 开 发 者 是 James Clark, 还 开 发 了 很 多 我 们 所 熟 知 的 工 具 包 : groff、 Jade、 XP (a Java XML parser package)、 and XT (a Java XSL engine)。 Expat XML Parser 概 述Expat 是 一 个 面 向 流 的
2、解 析 器 。 您 注 册 的 解 析 器 回 调 ( 或 handler) 功 能 , 然 后开 始 搜 索 它 的 文 档 。 当 解 析 器 识 别 该 文 件 的 指 定 的 位 置 , 它 会 调 用 该 部 分 相 应 的 处 理 程序 ( 如 果 您 已 经 注 册 的 一 个 ) 。 该 文 件 被 输 送 到 解 析 器 , 会 被 分 割 成 多 个 片 断 , 并 分 段装 到 内 存 中 。 因 此 expat 可 以 解 析 那 些 巨 大 的 文 件 。 如 何 使 用 它 们Expat XML Parser 支 持 设 置 多 种 不 同 的 处 理 器 。 但
3、是 要 使 用 它 们 , 你 只 需 要 学 习四 个 功 能 , 即 可 满 足 80%的 需 要 。 它 们 是 : XML_ParserCreate Create a new parser object. XML_SetElementHandler Set handlers for start and end tags. XML_SetCharacterDataHandler Set handler for text. XML_Parse Pass a buffer full of document to the parser开源的 XML Parser expat文章分类: C+编程
4、 expat 是使用 C 所写的 XML 解释器,采用流的方式来解析 XML 文件,并且基于事件通知型来调用分析到的数据,并不需要把所有 XML 文件全部加载到内存里,这样可以分析非常大的 XML 文件。由于 expat 库是由 XML 的主要负责人 James Clark 来实现的,因此它是符合 W3C 的 XML 标准的。 正因为源码全部是纯 C 所写,因此,非常容易移植,尤其是适用于嵌入式平台,我在往联芯的手机平台上移植时,几乎没改任何东西。 不过,优点也带来了缺点,因为是采用流的方式解析 XML,所以不会像 TinyXML 那样在一块内存中生成基于 DOM 的树。 虽然这样解析起来略显
5、麻烦,但是基于回调的机制,在我看来还是蛮方便的。下面就说使用方法: 首先是用 XML_ParserCreate(const XML_Char *encodingName),参数一般为 NULL,函数返回一个 XML_Parser 类型指针,我们就当他是一个句柄吧,类似于 Windows 里的内核对象,一般需要保存在一个全局的指针里。 然后调用 XML_SetElementHandler(XML_Parser parser, XML_StartElementHandler start, XML_EndElementHandler end) 第一个参数是那个 Parser 句柄,第二个和第三个参数
6、则是整个 Parser 的核心,类型为 CallBack 的函数,不了解 CallBack 函数的,我在这里简单说下,函数调用一般分为两种,一种是主调,即编写代码者自己调用的函数,还一种称为 Callback 函数,编码者写好,但他自己却不主动调用,而是在某些条件下(编码者并不清楚具体时间和流程),由其他函数调用,比如设备驱动,操作系统提供了一组某个设备的函数指针,比如 LCD 屏驱动,由一组画点,画线,画块等函数组成,当更换 LCD 时,只需要把操作系统开放的函数指针,指向你提供的接口即可,操作系统再需要时,会自动调用你的驱动函数,这就是回调函数一个典型的例子。 这二个回调分别是对应于解析和
7、之间的字段的回调。回调原型如下: typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData,const XML_Char *s,int len); 其中第二个参数是一块 Buffer 的指针,如果你单步 DEBUG 后,你会发现 expat 用的就是你传入的那块 Buffer(这块 Buffer 下面讲解),比如:天气 28 日 08 时至 29 日 08 时,陕西中南部、山西西南部、河南中南部、湖北北部、四川中东部、重庆西部和北部、贵州西部等地的部分地区有大雨或暴雨,河南南部、湖北北部等地局部有大暴雨。【点击“更多”查询
8、其他城市天气】 假设目前解析到天气这个 charData, 如果你看那个指针的所有内容的话,实际上是这样的:天气 28 日 08 时至 29 日 08 时,陕西中南部、山西西南部、河南中南部、湖北北部、四川中东部、重庆西部和北部、贵州西部等地的部分地区有大雨或暴雨,河南南部、湖北北部等地局部有大暴雨。【点击“更多”查询其他城市天气】 所以要根据第三个参数 len 来确定正确的数据。 但这里有个非常隐晦的问题,如果不知道的话,会带来很大麻烦,下面说。 最后就是 parse,调用 XML_Parse(XML_Parser parser, const char *s, int len, int is
9、Final) 第二个参数是用户指定的 Buffer 指针, 第三个是这块 Buffer 中实际内容的字节数,最后参数代表是否这块 Buffer 已经结束。比如要解析的 XML 文件太大,但内存比较吃紧,Buffer 比较小,则可以循环读取文件,然后丢给 Parser, 在文件读取结束前,isFinal参数为 FALSE,反之为 TRUE。 这里的 Buffer 如果太小则会造成上面提到那个隐晦的问题, XML_CharacterDataHandler 一次返回的可能并不是完整的 CharData,比如这个charData 的 Len 大于你的 Buffer 大小,那这是会连续调用 2 次XML
10、_CharacterDataHandler,我们需要将 2 次结果拼接起来,以得到正确结果,因此我们的状态机一定要考虑到这点。 顺便说下XML_ParserReset(XML_Parser parser, const XML_Char *encodingName)函数,在某些时候,如果你不确定前后 2 次 XML 是否一样的情况下,比如网络上投递的 XML,在一次解析后最好调用一次本函数,否则会出现意料之外的结果。比如前后两次 XML 完全一样,可这你并不知情,那么 XML_Parse()会返回失败。 XML 解析libxml 库函数解释libxml(一)绪论 Libxml 是一个实现读、创建
11、及操纵 XML 数据功能的 C 语言库。这个指南提供例子代码并给出它基本功能的解释。在这个项目的主页上有 Libxml 及更多关于它可用的资料。包含有完整的 API 文档。这个指南并不能替代这些完整的文档,但是阐明功能需要使用库来完成基本操作。 这个指南基于一个简单的 XML 应用,它使用我写的一篇文章生成,它包含有元数据和文章的主体。本指南中的例子代码示范如何做到: 解析文档 取得指定元素的文本 添加一个元素及它的内容 添加一个属性 取得一个属性的值 例子的完整代码包含在附录中 数据类型 Libxml 定义了许多数据类型,我们将反复碰到它们,它隐藏了杂乱的来源以致你不必处理它除非你有特定的需
12、要。xmlChar 替代 char,使用 UTF-8 编码的一字节字符串。如果你的数据使用其它编码,它必须被转换到 UTF-8 才能使用 libxml 的函数。在 libxml 编码支持 WEB 页面有更多关于编码的有用信息。 XmlDoc 包含由解析文档建立的树结构,xmlDocPtr 是指向这个结构的指针。xmlNodePtr and xmlNode 包含单一结点的结构 xmlNodePtr 是指向这个结构的指针,它被用于遍历文档树。 解析文档 解析文档时仅仅需要文件名并只调用一个函数,并有错误检查。完整代码:附录 C, Keyword 例程代码 xmlDocPtr doc; xmlNod
13、ePtr cur; doc = xmlParseFile(docname); if (doc = NULL ) fprintf(stderr,“Document not parsed successfully. n“); return; cur = xmlDocGetRootElement(doc); if (cur = NULL) fprintf(stderr,“empty documentn“); xmlFreeDoc(doc); return; if (xmlStrcmp(cur-name, (const xmlChar *) “story“) fprintf(stderr,“docum
14、ent of the wrong type, root node != story“); xmlFreeDoc(doc); return; 定义解析文档指针。 定义结点指针(你需要它为了在各个结点间移动)。 检查解析文档是否成功,如果不成功,libxml 将指一个注册的错误并停止。 注释 一个常见错误是不适当的编码。XML 标准文档除了用 UTF-8 或 UTF-16 外还可用其它编码保存。如果文档是这样,libxml 将自动地为你转换到 UTF-8。更多关于 XML 编码信息包含在XML 标准中。 取得文档根元素 检查确认当前文档中包含内容。 在这个例子中,我们需要确认文档是正确的类型。“S
15、tory”是在这个指南中使用文档的根类型。 取得元素内容 你找到在文档树中你要查找的元素后可以取得它的内容。在这个例子中我们查找“story”元素。进程将在冗长的树中查找我们感兴趣的元素。我们假定你已经有了一个名为 doc 的 xmlDocPtr 和一个名为 cur 的 xmlNodPtr。 cur = cur-xmlChildrenNode; while (cur != NULL) if (!xmlStrcmp(cur-name, (const xmlChar *)“storyinfo“) parseStory (doc, cur); cur = cur-next; 取得 cur 的第一个子
16、结点,cur 指向文档的根,即“story”元素。 这个循环迭代通过“story”的子元素查找“storyinfo”。这是一个包含有我们将查找的“keywords”的元素。它使用了 libxml 字符串比较函数 xmlStrcmp。如果相符,它调用函数 parseStory。 void parseStory (xmlDocPtr doc, xmlNodePtr cur) xmlChar *key; cur = cur-xmlChildrenNode; while (cur != NULL) if (!xmlStrcmp(cur-name, (const xmlChar *)“keyword“)
17、 key = xmlNodeListGetString(doc, cur-xmlChildrenNode, 1); printf(“keyword: %sn“, key); xmlFree(key); cur = cur-next; return; 再次取得第一个子结点。 像上面那个循环一样,我们能够迭代,查找我们感兴趣的叫做“keyword”的元素。 当我们找到元素“keyword”时,我们需要打印它包含在 XML 中的记录的内容,文本被包含于元素的子结点中,因此我们借助了 cur-xmlChildrenNode,为了取得文本,我们使用函数 xmlNodeListGetString,它有一个
18、文档指针参数,在这个例子中,我们仅仅打印它。注释 因为 xmlNodeListGetString 为它返回的字符串分配内存,你必须使用 xmlFree 释放它。 使用 XPath 取得元素内容除了一步步遍历文档树查找元素外,Libxml2 包含支持使用 Xpath 表达式取得指定结点集。完整的 Xpath API 文档在这里。Xpath 允许通过路径文档搜索匹配指定条件的结点。在下面的例子中,我们搜索文档中所有的“keyword”元素。 注释 下面是 Xpath 完整的讨论。它详细的使用资料,请查阅 Xpath 规范。 这个例子完整的代码参见附录 D,XPath 例程代码。 Using XPa
19、th requires setting up an xmlXPathContext and then supplying the XPath expression and the context to the xmlXPathEvalExpression function. The function returns an xmlXPathObjectPtr, which includes the set of nodes satisfying the XPath expression. 使用 XPath 需要安装 xmlXPathContext 才支持 XPath 表达式及 xmlXPathE
20、valExpression 函数,这个函数返回一个 xmlXPathObjectPtr,它包含有XPath 表达式的结点集。 xmlXPathObjectPtr getnodeset (xmlDocPtr doc, xmlChar *xpath) xmlXPathContextPtr context; xmlXPathObjectPtr result; context = xmlXPathNewContext(doc); result = xmlXPathEvalExpression(xpath, context); if(xmlXPathNodeSetIsEmpty(result-nodes
21、etval) printf(“No resultn“); return NULL; xmlXPathFreeContext(context); return result; 首先定义变量 初始化变量 context 应用 XPath 表达式 检查结果 由函数返回的 xmlPathObjectPtr 包含一个结点集和其它需要被迭代及操作的信息。在这个例子中我们的函数返回 xmlXPathObjectPtr,我们使用它打印我们文档中 keyword 结点的内容。这个结点集对象包含在集合(nodeNr)中的元素数目及一个结点(nodeTab)数组。 for (i=0; i nodeNr; i+) k
22、eyword = xmlNodeListGetString(doc, nodeset-nodeTabi-xmlChildrenNode, printf(“keyword: %sn“, keyword); xmlFree(keyword); 变量 nodeset-Nr 持有结点集中元素的数量。我们使用它遍历数组。 打印每个结点包含的内容。 注释 Note that we are printing the child node of the node that is returned, because the contents of the keyword element are a child
23、text node.注意我们打印的是结点的子结点的返回值,因为 keyword 元素的内容是一个子文本结点。写(插入)元素 写元素内容使用上面许多一样的步骤解析文档并遍历树。我们先解析文档然后遍历树查找我们想插入元素的位置。在这个例子中,我们再一次查找“storyinfo”元素并插入一个 keyword。然后我们装文件写入磁盘。完整代码:附录 E,添加 keyword 例程 本例中主要的不同在于 parseStory void parseStory (xmlDocPtr doc, xmlNodePtr cur, char *keyword) xmlNewTextChild (cur, NULL
24、, “keyword“, keyword); return; XmlNewTextChild 函数添加一个当前结点的新的子元素到树中 一旦结点被添加,我们应当写文档到文件中。你是否想给元素指定一个命名空间?你能添加它,在我们的例子中,命名空间是 NULL。 xmlSaveFormatFile (docname, doc, 1); 第一个参数是写入文件的名,你注意到和我们刚刚读入的文件名是一样的。在这个例子中,我们仅仅覆盖原来的文件。第二个参数是一个 xmlDoc 结构指针,第三个参数设定为 1,保证在输出上写入。 libxml(二)写属性 写属性类似于给一个新元素写文本。在这个例子中,我们将添
25、加一个 reference 结点URI 属性到我们的文档中。完整代码:附录 F,添加属性例程代码。reference 是 story 元素的一个子结点,所以找到并插入新元素及其属性是简单的。一旦我们在 parseDoc 进行了错误检查,我们将在正确的位置加放我们的新元素。但进行之前我们需要定义一个此前我们不见过的数据类型。 xmlAttrPtr newattr; 我们也需要 xmlNodePtr: xmlNodePtr newnode; 剩下的 parseDoc 则和前面一样,检查根结点是否为 story。如果是的,那我们知道我们将在指定的位置添加我们的元素。 newnode = xmlNew
26、TextChild (cur, NULL, “reference“, NULL); newattr = xmlNewProp (newnode, “uri“, uri); 使用 xmlNewTextChild 函数添国一个新结点到当前结点位置。 一旦结点被添加,文件应像前面的例子将我们添加的元素及文本内容写入磁盘。 取得属性 取得属性值类似于前面我们取得一个结点的文本内容。在这个例子中,我们将取出我们在前一部分添加的 URI 的值。完整代码:附录 G,取得属性值例程代码。这个例子的初始步骤和前面是类似的:解析文档,查找你感兴趣的元素,然后进入一个函数完成指定的请求任务。在这个例子中,我们调用
27、getReference。 void getReference (xmlDocPtr doc, xmlNodePtr cur) xmlChar *uri; cur = cur-xmlChildrenNode; while (cur != NULL) if (!xmlStrcmp(cur-name, (const xmlChar *)“reference“) uri = xmlGetProp(cur, “uri“); printf(“uri: %sn“, uri); xmlFree(uri); cur = cur-next; return; 关键函数是 xmlGetProp,它返回一个包含属性值
28、的 xmlChar。在本例中,我们仅仅打印它。 注释 如果你使用 DTD 定义属性的固定值或缺省值,这个函数也将取得它。 编码转换 数据编码兼容问题是程序员新建普通的 XML 或特定 XML 时最常见的困难。稍后的讨论来思考设计你的应用程序将帮助你避免这个困难。实际上,libxml 能以 UTF-8 格式保存和操纵多种数据 你的程序使用其它的数据格式,比如常见的 ISO-8859-1 编码,必须使用 libxml 函数转换到 UTF-8。如果你想你的程序以除 UTF-8 外的其它编码方式输出也必须做转换。 如果能有效地转换数据 Libxml 将使用转换器。无转换器时,仅仅 UTF-8、UTF-
29、16 和ISO-8859-1 能够被作为外部格式使用。有转换器时,它能将从其它格式与 UTF-8 互换的任何格式均可使用。当前转换器支持大约 150 种不同的编码格式之间的相互转换。实际支持的格式数量正在被实现。每一个实现在的转换器尽可能的支持每一种格式。 警告:一个常见错误是在内部数据不同的部分使用不同的编码格式。最常见的情况是一个用以 ISO-8859-1 作为内部数据格式,结合 libxml 部分使用 UTF-8 格式。结果是一个应用程序要面对不同地内部数据格式。一部分代码执行后,它或其它部分代码将使用曲解的数据。 这个例子构造一个简单的文档,然后添加在命令行提供的内容到根元素并使用适当
30、的编码将结果输出到标准输出设备上。在这个例子中,我们使用 ISO-8859-1 编码。在命令输入的内容将被从 ISO-8859-1 转换到 UTF-8。完整代码:附件 H,编码转换例程代码。 包含在例子中的转换函数使用 libxml 的 xmlFindCharEncodingHandler 函数。 xmlCharEncodingHandlerPtr handler; size = (int)strlen(in)+1; out_size = size*2-1; out = malloc(size_t)out_size); handler = xmlFindCharEncodingHandler(
31、encoding); handler-input(out, xmlSaveFormatFileEnc(“-“, doc, encoding, 1); 定义一个 xmlCharEncodingHandler 函数指针。 XmlCharEncodingHandler 函数需要给出输入和输出字符串的大小,这里计算输入输出字符串。 XmlFindCharEncodingHandler 使用数据初始编码作为参数搜索 libxml 已经完成的转换器句柄并将找到的函数指针返回,如果没有找到则返回 NULL。 The conversion function identified by handler requ
32、ires as its arguments pointers to the input and output strings, along with the length of each. The lengths must be determined separately by the application. 由句柄指定的转换函数请求输入、输出字符中及它们的长度作为参数。这个长度必须由应用程序分别指定。 用指定编码而不是 UTF-8 输出,我们使用 xmlSaveFormatFileEnc 指不定期编码方式。 libxml2 在 linux 下的使用官网地址:http:/xmlsoft.or
33、g1.下载和安装 LIBXML2Libxml2 是个 C 语言的 XML 程式库,能够简单方便的提供对 XML 文档的各种操作,并且支持 XPATH 查询,连同部分的支持 XSLT 转换等功能。Libxml2 的下载地址是http:/xmlsoft.org/,完全版的库是开源的,并且带有例子程式和说明文档。最好将这个库先下载下来,因为这样能够查看其中的文档和例子。由于我是在 linux 下用 C 语言进行研发的,所以我下载的是 libxml2-2.6.20.tar.gz版本的源码包。具体安装步骤:1、解压:$tar zxvf libxml2-2.6.20.tar.gz2、进入解压后的安装目录:
34、$cd libxml2-2.6.203、安装三部曲:1)$./configure2)$make3)$make install(安装完毕)2. Libxml2 中的数据类型和函数一个函数库中可能有几百种数据类型连同几千个函数,但是记住大师的话,90%的功能都是由 30%的内容提供的。对于 libxml2,我认为搞懂以下的数据类型和函数就足够了。2.1 内部字符类型 xmlCharxmlChar 是 Libxml2 中的字符类型,库中任何字符、字符串都是基于这个数据类型。事实上他的定义是:xmlstring.h-typedef unsigned char xmlChar;使用 unsigned c
35、har 作为内部字符格式是考虑到他能很好适应 UTF-8 编码,而 UTF-8 编码正是 libxml2 的内部编码,其他格式的编码要转换为这个编码才能在 libxml2 中使用。还经常能够看到使用 xmlChar*作为字符串类型,很多函数会返回一个动态分配内存的xmlChar*变量,使用这样的函数时记得要手动删除内存。2.2 xmlChar 相关函数如同标准 c 中的 char 类型相同,xmlChar 也有动态内存分配、字符串操作等相关函数。例如 xmlMalloc 是动态分配内存的函数;xmlFree 是配套的释放内存函数;xmlStrcmp 是字符串比较函数等等。基本上 xmlChar
36、 字符串相关函数都在 xmlstring.h 中定义;而动态内存分配函数在xmlmemory.h 中定义。2.3 xmlChar*和其他类型之间的转换另外要注意,因为总是要在 xmlChar*和 char*之间进行类型转换,所以定义了一个宏BAD_CAST,其定义如下:xmlstring.h-#define BAD_CAST (xmlChar *)。原则上来说,unsigned char 和 char 之间进行强制类型转换是没有问题的。2.4 文档类型 xmlDoc、指针 xmlDocPtrxmlDoc 是个 struct,保存了一个 xml 的相关信息,例如文档名、文档类型、子节点等等;xm
37、lDocPtr 等于 xmlDoc*,他搞成这个样子总让人以为是智能指针,其实不是,要手动删除的。 xmlNewDoc 函数创建一个新的文档指针。 xmlParseFile 函数以默认方式读入一个 UTF-8 格式的文档,并返回文档指针。 xmlReadFile 函数读入一个带有某种编码的 xml 文档,并返回文档指针;细节见libxml2 参考手册。 xmlFreeDoc 释放文档指针。特别注意,当您调用 xmlFreeDoc 时,该文档任何包含的节点内存都被释放,所以一般来说无需手动调用 xmlFreeNode 或 xmlFreeNodeList 来释放动态分配的节点内存,除非您把该节点从
38、文档中移除了。一般来说,一个文档中任何节点都应该动态分配,然后加入文档,最后调用 xmlFreeDoc 一次释放任何节点申请的动态内存,这也是为什么我们很少看见 xmlNodeFree 的原因。 xmlSaveFile 将文档以默认方式存入一个文档。 xmlSaveFormatFileEnc 可将文档以某种编码/格式存入一个文档中。2.5 节点类型 xmlNode、指针 xmlNodePtr节点应该是 xml 中最重要的元素了,xmlNode 代表了 xml 文档中的一个节点,实现为一个 struct,内容很丰富:tree.htypedef struct _xmlNode xmlNode;ty
39、pedef xmlNode *xmlNodePtr;struct _xmlNode void *_private; /* application data */xmlElementType type; /* type number, must be second ! */const xmlChar *name; /* the name of the node, or the entity */struct _xmlNode *children; /* parent-childs link */struct _xmlNode *last; /* last child link */struct
40、_xmlNode *parent; /* child-parent link */struct _xmlNode *next; /* next sibling link */struct _xmlNode *prev; /* previous sibling link */struct _xmlDoc *doc; /* the containing document */* End of common part */xmlNs *ns; /* pointer to the associated namespace */xmlChar *content; /* the content */str
41、uct _xmlAttr *properties; /* properties list */xmlNs *nsDef; /* namespace definitions on this node */void *psvi; /* for type/PSVI informations */unsigned short line; /* line number */unsigned short extra; /* extra data for XPath/XSLT */;能够看到,节点之间是以链表和树两种方式同时组织起来的,next 和 prev 指针能够组成链表,而 parent 和 chil
42、dren 能够组织为树。同时更有以下重要元素: 节点中的文字内容:content; 节点所属文档:doc; 节点名字:name; 节点的 namespace:ns; 节点属性列表:properties;Xml 文档的操作其根本原理就是在节点之间移动、查询节点的各项信息,并进行增加、删除、修改的操作。xmlDocSetRootElement 函数能够将一个节点配置为某个文档的根节点,这是将文档和节点连接起来的重要手段,当有了根结点以后,任何子节点就能够依次连接上根节点,从而组织成为一个 xml 树。2.6 节点集合类型 xmlNodeSet、指针 xmlNodeSetPtr节点集合代表一个由节点
43、组成的变量,节点集合只作为 Xpath 的查询结果而出现(XPATH 的介绍见后面),因此被定义在 xpath.h 中,其定义如下:/* A node-set (an unordered collection of nodes without duplicates).*/typedef struct _xmlNodeSet xmlNodeSet;typedef xmlNodeSet *xmlNodeSetPtr;struct _xmlNodeSet int nodeNr; /* number of nodes in the set */int nodeMax; /* size of the a
44、rray as allocated */xmlNodePtr *nodeTab; /* array of nodes in no particular order */* with_ns to check wether namespace nodes should be looked at */;能够看出,节点集合有三个成员,分别是节点集合的节点数、最大可容纳的节点数,连同节点数组头指针。对节点集合中各个节点的访问方式很简单,如下:xmlNodeSetPtr nodeset = XPATH 查询结果;for (int i = 0; i nodeNr; i+) nodeset-nodeTab;注
45、意,libxml2 是个 c 函数库,因此其函数和数据类型都使用 c 语言的方式来处理。假如是 c+,我想我宁愿用 STL 中的 vector 来表示一个节点集合更好,而且没有内存泄漏或溢出的担忧。3. 简单 xml 操作例子了解以上基本知识之后,就能够进行一些简单的 xml 操作了。当然,还没有涉及到内码转换(使得 xml 中能够处理中文)、xpath 等较复杂的操作。3.1 创建 xml 文档有了上面的基础,创建一个 xml 文档显得很简单,其流程如下:l 用 xmlNewDoc 函数创建一个文档指针 doc;l 用 xmlNewNode 函数创建一个节点指针 root_node;l 用
46、xmlDocSetRootElement 将 root_node 配置为 doc 的根结点;l 给 root_node 添加一系列的子节点,并配置子节点的内容和属性;l 用 xmlSaveFile 将 xml 文档存入文档;l 用 xmlFreeDoc 函数关闭文档指针,并清除本文档中任何节点动态申请的内存。注意,有多种方式能够添加子节点:第一是用 xmlNewTextChild 直接添加一个文本子节点;第二是先创建新节点,然后用 xmlAddChild 将新节点加入上层节点。源代码文档是 CreateXmlFile.cpp,如下:/*created: 2007/11/09created: 9
47、:11:2007 15:34filename: CreateXmlFile.cppauthor: Wang xuebin depend: libxml2.lib build: nmake TARGET_NAME=CreateXmlFilepurpose: 创建一个 xml 文档*/#include #include #include #include int main()/定义文档和节点指针xmlDocPtr doc = xmlNewDoc(BAD_CAST“1.0“);xmlNodePtr root_node = xmlNewNode(NULL,BAD_CAST“root“);/配置根节点x
48、mlDocSetRootElement(doc,root_node);/在根节点中直接创建节点xmlNewTextChild(root_node, NULL, BAD_CAST “newNode1“, BAD_CAST “newNode1 content“);xmlNewTextChild(root_node, NULL, BAD_CAST “newNode2“, BAD_CAST “newNode2 content“);xmlNewTextChild(root_node, NULL, BAD_CAST “newNode3“, BAD_CAST “newNode3 content“);/创建一个节点,配置其内容和属性,然后加入根结点xmlNodePtr node = xmlNewNode(NULL,BAD_CAST“node2“);xmlNodePtr content = xmlNewText(B