libxml库解析XML文件

  
绪论
Libxml
是一个有免费许可的用于处理 XML 、可以轻松跨越多个平台的 C 语言库。 Libxml 实现了读、创建及操纵 XML 数据功能。
  
这个指南基于一个简单的 XML 应用。

数据类型
Libxml
定义了许多数据类型,它隐藏了杂乱的来源以致你不必处理它除非你有特定的需要。 xmlChar  替代 char, 使用 UTF-8 编码的一字节字符串。如果你的数据使用其它编码,它必须被转换到 UTF-8 才能使用 libxml 的函数。在 libxml 编码支持 WEB 页面有更多关于编码的有用信息。
XmlDoc
包含由解析文档建立的树结构, xmlDocPtr 是指向这个结构的指针。
xmlNodePtr and xmlNode
包含单一结点的结构 xmlNodePtr 是指向这个结构的指针,它被用于遍历文档树。
 
解析文档
解析文档时仅仅需要文件名并只调用一个函数,并有错误检查。
xmlDocPtr doc;
xmlNodePtr cur;
doc = xmlParseFile(docname);
if (doc == NULL ) {
    fprintf(stderr,"Document not parsed successfully. /n");
    return;
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
    fprintf(stderr,"empty document/n");
    xmlFreeDoc(doc);
    return;
}
if (xmlStrcmp(cur->name, (const xmlChar *) "story")) {
    fprintf(stderr,"document of the wrong type, root node != story");
    xmlFreeDoc(doc);
    return;
}
①定义解析文档指针。
②定义结点指针 ( 需要它为了在各个结点间移动 )
④检查解析文档是否成功,如果不成功, libxml 将指一个注册的错误并停止。
注释
一个常见错误是不适当的编码。 XML 标准文档除了用 UTF-8 UTF-16 外还可用其它编码保存。如果文档是这样, libxml 将自动地为你转换到 UTF-8
⑤取得文档根元素
⑥检查确认当前文档中包含内容。
⑦在这个例子中,我们需要确认文档是正确的类型。“ Story ”是在这个指南中使用文档的根类型。
 
取得元素内容
找到在文档树中要查找的元素后可以取得它的内容。在这个例子中我们查找“ 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 的第一个子结点, 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"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("keyword: %s/n", key);
xmlFree(key);
}
cur = cur->next;
}
return;
}
再次取得第一个子结点。
像上面那个循环一样,我们能过迭代,查找我们感兴趣的叫做“ keyword ”的元素。
当我们找到元素“ keyword ”时,我们需要打印它包含在 XML 中的记录的内容,文本被包含于元素的子结点中,因此我们借助了 cur-> xmlChildrenNode ,为了取得文本,我们使用函数 xmlNodeListGetString ,它有一个文档指针参数,在这个例    子中,我们仅仅打印它。
注释
因为 xmlNodeListGetString 为它返回的字符串分配内存,你必须使用 xmlFree 释放它。
 
使用 XPath 取得元素内容
除了一步步遍历文档树查找元素外, Libxml2 包含支持使用 Xpath 表达式取得指定结点集。完整的 Xpath API 文档在这里。 Xpath 允许通过路径文档搜索匹配指定条件的结点。在下面的例子中,我们搜索文档中所有的“ keyword ”元素。  
注释
下面是 Xpath 完整的讨论。它详细的使用资料,请查阅 Xpath 规范。
这个例子完整的代码参见附录 D XPath 例程代码。
Using XPath 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 表达式及 xmlXPathEvalExpression 函数,这个函数返回一个 xmlXPathObjectPtr ,它包含有
XPath 表达式的结点集。
 
xmlXPathObjectPtr
getnodeset (xmlDocPtr doc, xmlChar *xpath){
xmlXPathContextPtr context;
xmlXPathObjectPtr result;
context = xmlXPathNewContext(doc);
result = xmlXPathEvalExpression(xpath, context);
if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
printf("No result/n");
return NULL;
}
xmlXPathFreeContext(context);
return result;
}
①首先定义变量
②初始化变量 context
③应用 XPath 表达式
④检查结果
由函数返回的 xmlPathObjectPtr 包含一个结点集和其它需要被迭代及操作的信息。在这个例子中我们的函数返回 xmlXPathObjectPtr ,我们使用它打印我们文档中 keyword 结点的内容。这个结点集对象包含在集合 (nodeNr) 中的元素数目及一个结点 (nodeTab) 数组。
 
for (i=0; i < nodeset->nodeNr; i++) {
keyword = xmlNodeListGetString(doc,
nodeset->nodeTab[i]->xmlChildrenNode, printf("keyword: %s/n", 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 text node.
注意我们打印的是结点的子结点的返回值,因为 keyword 元素的内容是一个子文本结点。

写元素
写元素内容使用上面许多一样的步骤—解析文档并遍历树。我们先解析文档然后遍历树查找我们想插入元素的位置。在这个例子中,我们再一次查找“ storyinfo
”元素并插入一个 keyword 。然后我们装文件写入磁盘。完整代码:附录 E ,添加 keyword 例程
本例中主要的不同在于 parseStory
void
parseStory (xmlDocPtr doc, xmlNodePtr cur, char *keyword) {
xmlNewTextChild (cur, NULL, "keyword", keyword);
return;
}
XmlNewTextChild 函数添加一个当前结点的新的子元素到树中
一旦结点被添加,我们应当写文档到文件中。你是否想给元素指定一个命名空间?你能添加它,在我们的例子中,命名空间是 NULL
 
xmlSaveFormatFile (docname, doc, 1);
 
第一个参数是写入文件的名,你注意到和我们刚刚读入的文件名是一样的。在这个例子中,我们仅仅覆盖原来的文件。第二个参数是一个 xmlDoc 结构指针,第三个参数设定为 1 ,保证在输出上写入。
 
写属性
写属性类似于给一个新元素写文本。在这个例子中,我们将添加一个 reference 结点 URI 属性到我们的文档中。 reference story 元素的一个子结点,所以找到并插入新元素及其属性是简单的。一旦我们在 parseDoc 进行了错误检查,我们将在正确的位置加放我们的新元素。但进行之前我们需要定义一个此前我们不见过的数据类型。
 
xmlAttrPtr newattr;
 
我们也需要 xmlNodePtr
xmlNodePtr newnode;
 
剩下的 parseDoc 则和前面一样,检查根结点是否为 story 。如果是的,那我们知道我们将在指定的位置添加我们的元素。   
newnode = xmlNewTextChild (cur, NULL, "reference", NULL);
newattr = xmlNewProp (newnode, "uri", uri);
 
①使用 xmlNewTextChild 函数添国一个新结点到当前结点位置。
一旦结点被添加,文件应像前面的例子将我们添加的元素及文本内容写入磁盘。
 
取得属性
取得属性值类似于前面我们取得一个结点的文本内容。在这个例子中,我们将取出我们在前一部分添加的 URI 的值。

  
这个例子的初始步骤和前面是类似的:解析文档,查找你感兴趣的元素,然后进入一个函数完成指定的请求任务。在这个例子中,我们调用 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: %s/n", uri);
xmlFree(uri);
}
cur = cur->next;
}
return;
}
 
  关键函数是 xmlGetProp, 它返回一个包含属性值的 xmlChar 。在本例中,我们仅仅打印它。
 
注释
如果你使用 DTD 定义属性的固定值或缺省值,这个函数也将取得它。
 
编码转换
数据编码兼容问题是程序员新建普通的 XML 或特定 XML 时最常见的困难。按照这里
   稍后的讨论来思考设计你的应用程序将帮助你避免这个困难。实际上, libxml 能以 UTF-8 格式保存和操纵多种数据  
你的程序使用其它的数据格式,比如常见的 ISO-8859-1 编码,必须使用 libxml 函数转换到 UTF-8 。如果你想你的程序以除 UTF-8 外的其它编码方式输出也必须做转换。  
   
如果能有效地转换数据 Libxml 将使用转换器。无转换器时,仅仅 UTF-8 UTF-16 ISO-8859-1 能够被作为外部格式使用。有转换器时,它能将从其它格式与 UTF-8 互换的任何格式均可使用。当前转换器支持大约 150 种不同的编码格式之间的相互转换。实际支持的格式数量正在被实现。每一个实现在的转换器尽可能的支持每一种格式。
 
警告
一个常见错误是在内部数据不同的部分使用不同的编码格式。最常见的是情况是一个应用以 ISO-8859-1 作为内部数据格式,结合 libxml 部分使用 UTF-8 格式。结果是一个应用程序要面对不同地内部数据格式。一部分代码执行后,它或其它部分代码将使用曲解的数据。
  
这个例子构造一个简单的文档,然后添加在命令行提供的内容到根元素并使用适当的编码将结果输出到标准输出设备上。在这个例子中,我们使用 ISO-8859 -1 编码。在命令输入的内容将被从 ISO-8859-1 转换到 UTF-8
 
  
包含在例子中的转换函数使用 libxml xmlFindCharEncodingHandler 函数。
 
xmlCharEncodingHandlerPtr handler;
size = (int)strlen(in)+1;
out_size = size*2-1;
out = malloc((size_t)out_size);

handler = xmlFindCharEncodingHandler(encoding);

handler->input(out, &out_size, in, &temp);

xmlSaveFormatFileEnc("-", doc, encoding, 1);
 
①定义一个 xmlCharEncodingHandler 函数指针。
XmlCharEncodingHandler 函数需要给出输入和输出字符串的大小,这里计算输入输出字符串。
XmlFindCharEncodingHandler 使用数据初始编码作为参数搜索 libxml 已经完成的转换器句柄并将找到的函数指针返回,如果没有找到则返回 NULL
The conversion function identified by handler requires 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 指不定期编码方式。  
 
A.
编译
Libxml
包含一个脚本 xml2-config ,它一般用于编译和链接程序到库时产生标志。
  为了取得预处理和编译标志,使用 xml2-config cflags ,为了取得链接标志,使用 xml2-config libs 。其它有效的参数请使用 xml2-config help 查阅。
 
B.
示例文档
<?xml version="1.0"?>
<story>
<storyinfo>
<author>John Fleck</author>
<datewritten>June 2, 2002</datewritten>
<keyword>example keyword</keyword>
</storyinfo>
<body>
<headline>This is the headline</headline>
<para>This is the body text.</para>
</body>
</story>
C. Keyword
例程代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
void
parseStory (xmlDocPtr doc, xmlNodePtr cur) {
xmlChar *key;
cur = cur->xmlChildrenNode;
while (cur != NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"keyword"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
printf("keyword: %s/n", key);
xmlFree(key);
}
cur = cur->next;
}
return;
}
static void
parseDoc(char *docname) {
xmlDocPtr doc;
xmlNodePtr cur;
doc = xmlParseFile(docname);
if (doc == NULL ) {
fprintf(stderr,"Document not parsed successfully. /n");
return;
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
fprintf(stderr,"empty document/n");
xmlFreeDoc(doc);
return;
}
if (xmlStrcmp(cur->name, (const xmlChar *) "story")) {
fprintf(stderr,"document of the wrong type, root node != story");
xmlFreeDoc(doc);
return;
}
cur = cur->xmlChildrenNode;
while (cur != NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"storyinfo"))){
parseStory (doc, cur);
}
cur = cur->next;
}
xmlFreeDoc(doc);
return;
}
int
main(int argc, char **argv) {
char *docname;
if (argc <= 1) {
printf("Usage: %s docname/n", argv[0]);
return(0);
}
docname = argv[1];
parseDoc (docname);
return (1);
}
libxml (三)                                       
D. XPath
例程代码
#include <libxml/parser.h>
#include <libxml/xpath.h>
xmlDocPtr
getdoc (char *docname) {
xmlDocPtr doc;
doc = xmlParseFile(docname);
if (doc == NULL ) {
fprintf(stderr,"Document not parsed successfully. /n");
return NULL;
}  
return doc;
}
xmlXPathObjectPtr
getnodeset (xmlDocPtr doc, xmlChar *xpath){
xmlXPathContextPtr context;
xmlXPathObjectPtr result;
context = xmlXPathNewContext(doc);
result = xmlXPathEvalExpression(xpath, context);
if(xmlXPathNodeSetIsEmpty(result->nodesetval)){
printf("No result/n");
return NULL;
}
xmlXPathFreeContext(context);
return result;
}
int
main(int argc, char **argv) {
char *docname;
xmlDocPtr doc;
xmlChar *xpath = ("//keyword");
xmlNodeSetPtr nodeset;
xmlXPathObjectPtr result;
int i;
xmlChar *keyword;
if (argc <= 1) {
printf("Usage: %s docname/n", argv[0]);
return(0);
}
docname = argv[1];
doc = getdoc(docname);
result = getnodeset (doc, xpath);
if (result) {
nodeset = result->nodesetval;
for (i=0; i < nodeset->nodeNr; i++) {
keyword = xmlNodeListGetString(doc, nodeset->nodeTab[i]->printf
("keyword: %s/n", keyword);
xmlFree(keyword);
}
xmlXPathFreeObject (result);
}
xmlFreeDoc(doc);
xmlCleanupParser();
return (1);
}
E. 添加 keyword 例程代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
void
parseStory (xmlDocPtr doc, xmlNodePtr cur, char *keyword) {
xmlNewTextChild (cur, NULL, "keyword", keyword);
return;
}
xmlDocPtr
parseDoc(char *docname, char *keyword) {
xmlDocPtr doc;
xmlNodePtr cur;
doc = xmlParseFile(docname);
if (doc == NULL ) {
fprintf(stderr,"Document not parsed successfully. /n");
return (NULL);
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
fprintf(stderr,"empty document/n");
xmlFreeDoc(doc);
return (NULL);
}
if (xmlStrcmp(cur->name, (const xmlChar *) "story")) {
fprintf(stderr,"document of the wrong type, root node != story");
xmlFreeDoc(doc);
return (NULL);
}
cur = cur->xmlChildrenNode;
while (cur != NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"storyinfo"))){
parseStory (doc, cur, keyword);
}
cur = cur->next;
}
return(doc);
}
int
main(int argc, char **argv) {
char *docname;
char *keyword;
xmlDocPtr doc;
if (argc <= 2) {
printf("Usage: %s docname, keyword/n", argv[0]);
return(0);
}
docname = argv[1];
keyword = argv[2];
doc = parseDoc (docname, keyword);
if (doc != NULL) {
xmlSaveFormatFile (docname, doc, 0);
xmlFreeDoc(doc);
}
return (1);
}
F.
添加属性例程代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
xmlDocPtr
parseDoc(char *docname, char *uri) {
xmlDocPtr doc;
xmlNodePtr cur;
xmlNodePtr newnode;
xmlAttrPtr newattr;
doc = xmlParseFile(docname);
if (doc == NULL ) {
fprintf(stderr,"Document not parsed successfully. /n");
return (NULL);
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
fprintf(stderr,"empty document/n");
xmlFreeDoc(doc);
return (NULL);
}
if (xmlStrcmp(cur->name, (const xmlChar *) "story")) {
fprintf(stderr,"document of the wrong type, root node != story");
xmlFreeDoc(doc);
return (NULL);
}
newnode = xmlNewTextChild (cur, NULL, "reference", NULL);
newattr = xmlNewProp (newnode, "uri", uri);
return(doc);
}
int
main(int argc, char **argv) {
char *docname;
char *uri;
xmlDocPtr doc;
if (argc <= 2) {
printf("Usage: %s docname, uri/n", argv[0]);
return(0);
}
docname = argv[1];
uri = argv[2];
doc = parseDoc (docname, uri);
if (doc != NULL) {
xmlSaveFormatFile (docname, doc, 1);
xmlFreeDoc(doc);
}
return (1);
}
G.
取得属性值例程代码
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
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: %s/n", uri);
xmlFree(uri);
}
cur = cur->next;
}
return;
}
void
parseDoc(char *docname) {
xmlDocPtr doc;
xmlNodePtr cur;
doc = xmlParseFile(docname);
if (doc == NULL ) {
fprintf(stderr,"Document not parsed successfully. /n");
return;
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
fprintf(stderr,"empty document/n");
xmlFreeDoc(doc);
return;
}  
if (xmlStrcmp(cur->name, (const xmlChar *) "story")) {
fprintf(stderr,"document of the wrong type, root node != story");
xmlFreeDoc(doc);
return;
}
getReference (doc, cur);
xmlFreeDoc(doc);
return;
}
int
main(int argc, char **argv) {
char *docname;
if (argc <= 1) {
printf("Usage: %s docname/n", argv[0]);
return(0);
}
docname = argv[1];
parseDoc (docname);
return (1);
}
H.
编码转换例程代码
#include <string.h>
#include <libxml/parser.h>
unsigned char*
convert (unsigned char *in, char *encoding)
{
unsigned char *out;
int ret,size,out_size,temp;
xmlCharEncodingHandlerPtr handler;
size = (int)strlen(in)+1;
out_size = size*2-1;
out = malloc((size_t)out_size);
if (out) {
handler = xmlFindCharEncodingHandler(encoding);
if (!handler) {
free(out);
out = NULL;
}
}
if (out) {
temp=size-1;
ret = handler->input(out, &out_size, in, &temp);
if (ret || temp-size+1) {
if (ret) {  
printf("conversion wasn't successful./n");
} else {
printf("conversion wasn't successful. converted: }
free(out);
out = NULL;
} else {
out = realloc(out,out_size+1);
out[out_size]=0; /*null terminating out*/
}
} else {
printf("no mem/n");
}
return (out);
}
int
main(int argc, char **argv) {
unsigned char *content, *out;
xmlDocPtr doc;
xmlNodePtr rootnode;
char *encoding = "ISO-8859-1";
if (argc <= 1) {
printf("Usage: %s content/n", argv[0]);
return(0);
}
content = argv[1];
out = convert(content, encoding);
doc = xmlNewDoc ("1.0");
rootnode = xmlNewDocNode(doc, NULL, (const xmlChar*)"root", out);
xmlDocSetRootElement(doc, rootnode);
xmlSaveFormatFileEnc("-", doc, encoding, 1);
return (1);

 …………………………………………………………………………………………
char *convert(char *instr,char *encoding)
{
xmlCharEncodingHandlerPtr handler;
xmlBufferPtr in,out;
handler = xmlFindCharEncodingHandler(encoding);
if(NULL != handler)
{
in = xmlBufferCreate();
xmlBufferWriteChar(in,instr);
out = xmlBufferCreate();
if(xmlCharEncInFunc(handler, out, in)
0)
{
xmlBufferFree(in);
xmlBufferFree(out);
return NULL;
}
else
{
xmlBufferFree(in);
return (char *)out-
content;
}
}
}
 
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值