在xml里增加节点时添加回车(libxml2)

14 篇文章 4 订阅

上一个 关于libxml2的一篇文章中 F. 添加属性例程代码  ,添加的keyword结点后面没有回车,
跟后面的结点挤在一行了,不是很好看。
例如,有以下的xml例子文件
 1 <? xml version="1.0" ?>
 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文件的表示如下:

 1 <? xml version="1.0" ?>
 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>hello</keyword1><keyword2>hello</keyword2><keyword3>hello</keyword3></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后,就可以自动添加
回车了。

那,重新修正了的例子程序是如下那样,里面只修改了两条语句。

 1 #include <stdio.h>
 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文件里的某些结点元素的值的简单的例子。
 1     xmlNodePtr element;
 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函数的代码片断:
 1 switch (cur->type)  {
 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(http://blog.csdn.net/shanzhizi)的另一部分代码里,删除节点的程序去不是这样做。
例如,在一个结点被删除后,通常会使用xmlFreeDoc函数来释放该结点,恰好在这段
代码里却是判断如果该字符串不再字典缓冲区才去释放它,也就是调用宏DICT_FREE
来完成释放工作,这儿正好与前面的相反,很难理解为什么会产生矛盾。
宏DICT_FREE的代码:
 1 /**//**
 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);

这样的话,最开始的那段程序运行起来就没有任何问题了。

还有,xmlsoft上还有很多使用libxml2的例子程序,可以参考一下:

http://xmlsoft.org/examples/index.html

来自:http://www.cppblog.com/lymons/archive/2009/04/03/37553.html

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值