隐锋同学的blog上有关于libxml2的一篇文章,正好最近要使用这个库来处理xml文件。
不过在测试时我们发现用文章里F. 添加属性例程代码 时,添加的keyword结点后面没有回车,
跟后面的结点挤在一行了,不是很好看。
例如,有以下的xml例子文件
2 < BODY >
3 < filesystem >
4 < filesystemKeyData >
5 < filesystemName > Ext3 filesystemName >
6 < versionNumber > 123 versionNumber >
7 < option > good option >
8 filesystemKeyData >
9 < timestampSec > 456 timestampSec >
10 < status > heasjdkfjaskdfjsk status >
11 filesystem >
12 < filesystem >
13 < filesystemKeyData >
14 < filesystemName > Ext3 filesystemName >
15 < versionNumber > 123 versionNumber >
16 < option > good option >
17 filesystemKeyData >
18 < timestampSec > 456 timestampSec >
19 < status > heasjdkfjaskdfjsk status >
20 filesystem >
21 BODY >
例如,使用该文章例子中的代码在上面的filesystem节点的最后插入一个keyword的子结点后的,
该xml文件的表示如下:
2 < BODY >
3 < filesystem >
4 < filesystemKeyData >
5 < filesystemName > Ext3 filesystemName >
6 < versionNumber > 123 versionNumber >
7 < option > good option >
8 filesystemKeyData >
9 < timestampSec > 456 timestampSec >
10 < status > heasjdkfjaskdfjsk status >
11 < keyword1 > hello keyword1 >< keyword2 > hello keyword2 >< keyword3 > hello keyword3 > filesystem >
12 < filesystem >
13 < filesystemKeyData >
14 < filesystemName > Ext3 filesystemName >
15 < versionNumber > 123 versionNumber >
16 < option > good option >
17 filesystemKeyData >
18 < timestampSec > 456 timestampSec >
19 < status > heasjdkfjaskdfjsk status >
20 < keyword1 > hello keyword1 >< keyword2 > hello keyword2 >< keyword3 > hello keyword3 > filesystem >
21 BODY >
你会发现keyword和/filesystem像下面那样被挤在一起了,这并不是我们想要的.
<keyword1>hellokeyword1><keyword2>hellokeyword2><keyword3>hellokeyword3>filesystem>
通过设定 xmlKeepBlanksDefault(0) 以及 xmlSaveFormatFile(...)的format参数设置成1,都无法实现
在新追加的结点后面添加回车换行。
在www.xmlsoft.org的官方网站的maillist里关于这方面的信息非常少。但是,对我帮助最大还是
http://mail.gnome.org/archives/xml/2007-May/msg00043.html 这个问题里的例子代码,里面在设置
属性的时候用的xmlReadFile函数,而且options参数设定的是XML_PARSE_NOBLANKS。
于是,我们用xmlReadFile(...),把它的options参数设定成XML_PARSE_NOBLANKS后,就可以自动添加
回车了。
那,重新修正了的例子程序是如下那样,里面只修改了两条语句。
2 #include < string .h >
3 #include < stdlib.h >
4 #include < libxml / xmlmemory.h >
5 #include < libxml / parser.h >
6 void
7 parseStory (xmlDocPtr doc, xmlNodePtr cur, char * keyword)
8 {
9 xmlNewTextChild (cur, NULL, "keyword1", keyword);
10 xmlNewTextChild (cur, NULL, "keyword2", keyword);
11 xmlNewTextChild (cur, NULL, "keyword3", keyword);
12 return;
13}
14
15 xmlDocPtr
16 parseDoc ( char * docname, char * keyword)
17 {
18 xmlDocPtr doc;
19 xmlNodePtr cur;
20 //doc = xmlParseFile (docname);
21 doc = xmlReadFile(docname, NULL, XML_PARSE_NOBLANKS); //读取xml文件时忽略空格
22 if (doc == NULL)
23 {
24 fprintf (stderr, "Document not parsed successfully. \n");
25 return (NULL);
26 }
27 cur = xmlDocGetRootElement (doc);
28 if (cur == NULL)
29 {
30 fprintf (stderr, "empty document\n");
31 xmlFreeDoc (doc);
32 return (NULL);
33 }
34 if (xmlStrcmp (cur->name, (const xmlChar *) "BODY"))
35 {
36 fprintf (stderr, "document of the wrong type, root node != story\n");
37 xmlFreeDoc (doc);
38 return (NULL);
39 }
40 cur = cur->xmlChildrenNode;
41 while (cur != NULL)
42 {
43 if ((!xmlStrcmp (cur->name, (const xmlChar *) "filesystem")))
44 {
45 parseStory (doc, cur, keyword);
46 }
47 cur = cur->next;
48 }
49 return (doc);
50}
51
52 int
53 main ( int argc, char ** argv)
54 {
55 char *docname;
56 char *keyword;
57 xmlDocPtr doc;
58 if (argc <= 2)
59 {
60 printf ("Usage: %s docname, keyword\n", argv[0]);
61 return (0);
62 }
63 docname = argv[1];
64 keyword = argv[2];
65 doc = parseDoc (docname, keyword);
66 if (doc != NULL)
67 {
68 //xmlSaveFormatFile (docname, doc, 0);
69 xmlSaveFormatFile (docname, doc, 1);
70 xmlFreeDoc (doc);
71 }
72 return (1);
73}
74
修正1:是把xmlParseFile替换成xmlReadFile,并且是options参数设定成XML_PARSE_NOBLANKS;否则的话是不会在结点后面添加回车的。
修正2:把xmlSaveFormatFile的format参数修改成1,否则在使用xmlReadFile打开的xml文件时,在生成的xml文件里是会把所有的结点都放到一行里显示。
另外:xmlKeepBlanksDefault(0) 除了在读入xml文件时忽略空白之外,还会在写出xml文件时在每行前面放置缩进(indent)。如果使用xmlKeepBlanksDefault(1) 则你会发现每行前面的缩进就没有了,但不会影响回车换行。
//
额外话题: 更新结点的值得时候segement fault错误
下面的代码是更新XML文件里的某些结点元素的值的简单的例子。
2 //
3 xmlNodePtr childrenNodePtr = element -> children;
4 while (childrenNodePtr != NULL)
5 {
6 if(childrenNodePtr->type == XML_TEXT_NODE)
7 {
8 xmlNodeSetContent(childrenNodePtr, (const xmlChar*)"world");
9 return NORMAL_RET;
10 }
11 childrenNodePtr = childrenNodePtr->next;
12 }
运行该段代码,有时候会在使用libxml2的API函数xmlNodeSetContent
处发生段错误,但不是100%发生。
只有该结点在原来值是某些字符串的时候会发生该错误,比如说,
原来的值是"zo"的时候就会让程序崩溃。
阅读了libxml2的源代码发现,xmlNodeSetContent函数,在把结点值
设置成新的字符串之前会调用xmlFree(cur->content)来释放掉原来
字符串缓冲区的内存。
xmlNodeSetContent函数的代码片断:
2 case XML_DOCUMENT_FRAG_NODE:
3 case XML_ELEMENT_NODE:
4 case XML_ATTRIBUTE_NODE:
5 if (cur->children != NULL) xmlFreeNodeList(cur->children);
6 cur->children = xmlStringGetNodeList(cur->doc, content);
7 UPDATE_LAST_CHILD_AND_PARENT(cur)
8 break;
9 case XML_TEXT_NODE:
10 case XML_CDATA_SECTION_NODE:
11 case XML_ENTITY_REF_NODE:
12 case XML_ENTITY_NODE:
13 case XML_PI_NODE:
14 case XML_COMMENT_NODE:
15 if ((cur->content != NULL) &&
16 (cur->content != (xmlChar *) &(cur->properties))) {
17 if (!((cur->doc != NULL) && (cur->doc->dict != NULL) &&
18 (xmlDictOwns(cur->doc->dict, cur->content))))
19 xmlFree(cur->content);
20 }
在上面代码里,如果结点值得字符串如果在libxml2的字典缓冲区(cur->doc->dict)里,
就把该字符串释放掉。而原来的字符串"zo"恰好在它的字典缓冲里,那这样传递到
xmlFree函数里的地址是冲区的一部分而不是缓冲区的首地址的话,free函数当然
会死掉了。如果换成其他的字符串就没有任何问题。
但是,令人不解的是在libxml2的另一部分代码里,删除节点的程序去不是这样做。
例如,在一个结点被删除后,通常会使用xmlFreeDoc函数来释放该结点,恰好在这段
代码里却是判断如果该字符串不再字典缓冲区才去释放它,也就是调用宏DICT_FREE
来完成释放工作,这儿正好与前面的相反,很难理解为什么会产生矛盾。
宏DICT_FREE的代码:
2 * DICT_FREE:
3 * @str: a string
4 *
5 * Free a string if it is not owned by the "dict" dictionnary in the
6 * current scope
7 */
8 #define DICT_FREE(str) \
9 if ((str) && ((!dict) || \
10 (xmlDictOwns(dict, (const xmlChar *)(str)) == 0))) \
11 xmlFree((char *)(str));
上面这段代码判断是该字符串如果不在字典缓冲里才去释放。
在考虑这是否是xmlNodeSetContent函数的bug,不过在maillist和bugzilla也没有翻到关于它
的任何说明。
开发时间上也不允许去跟libxml2深究它是否是bug,只要采用了迂回策略了。
想办法不让libxml2产生字典缓冲不就可以了吗。
通过官方手册我们可以知道,xmlReadFile函数可以附加XML_PARSE_NODICT选项
来避免产生字典缓冲。就像下面这样:
doc = xmlReadFile(docname, NULL, XML_PARSE_NOBLANK | XML_PARSE_NODICT);
这样的话,最开始的那段程序运行起来就没有任何问题了。
隐锋同学的文章在这里:http://www.cnblogs.com/coolattt/articles/804112.html