隐锋同学的blog上有关于libxml2的一篇文章,正好最近要使用这个库来处理xml文件。
不过在测试时我们发现用文章里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>
 6void
 7parseStory (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
15xmlDocPtr
16parseDoc (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
52int
53main (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:把xmlSaveFormatFileformat参数修改成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函数的代码片断:

 1switch (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的另一部分代码里,删除节点的程序去不是这样做。
例如,在一个结点被删除后,通常会使用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);

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

隐锋同学的文章在这里:http://www.cnblogs.com/coolattt/articles/804112.html

转载于:https://blog.51cto.com/laokaddk/593043

在xml里追加结点时添加回车(libxml2)相关推荐

  1. php 添加cdata,php生成xml时添加CDATA标签的方法

    php生成xml时添加CDATA标签的方法 发布于 2014-12-14 17:18:59 | 210 次阅读 | 评论: 0 | 来源: 网友投递 XML可扩展标记语言xml 即 可扩展标记语言 . ...

  2. php cdata,php生成xml时添加CDATA标签

    php生成xml时添加CDATA标签方法非常的简单,因为是一个在xml中可以存储各种内容的标签了,下面整理了一个例子希望对各位有帮助. 贴上代码留住伤疤,不要把当成前后缀,其实它可以是标签,代码如下: ...

  3. android 重复申请权限,去除AndroidManifest.xml里重复申请的权限(uses-permission)

    2017年的第一篇文章,新的起点,不忘初心 花了点时间,解决了最近遇到的一个问题: 在引用第三方的服务时都会在Manifest中添加权限,一般都是无脑的复制粘贴,久而久之,项目里重复的权限越来越多 在 ...

  4. 用Java Instrumentation 在类加载时添加记录

    用Java Instrumentation 在类加载时添加记录 发布者:xanadu0214   来源:网络转载   发布日期:2013年11月06日   Java学习交流群:471651004 在分 ...

  5. IDEA里运行代码时出现Caused by: java.lang.ClassNotFoundException: org.apache.log4j.Logger的解决办法(图文详解)...

    问题详情 运行出现log4j的问题 "C:\Program Files\Java\jdk1.8.0_66\bin\java" -Didea.launcher.port=7533 & ...

  6. linux svn强制注释,svn强制提交时添加注释

    svn强制提交时添加注释 1.  需求 领导要求开发在提交代码时必须添加注释,经过查资料,实验,然后实现领导的要求. 2.  查看资料 参考svn文档,以及网上博客 通过查看资料,一步一步的实现脚本 ...

  7. 若依管理系统导出Excel时添加没有的列和关联码表显示中文进行导出

    场景 在使用若依后台管理系统时对于单表会自动生成导出相关的接口. 此时如果直接进行导出会导出相应的实体类中添加了 @Excel注解的属性. 自动生成导出接口示例: @GetMapping(" ...

  8. 在.NET里用XSLT时怎么使用msxsl:script

    来源:在.NET里用XSLT时怎么使用msxsl:script (思归呓语) 今天在CSDN论坛上看到一个网友在请教关于msxsl:script的用法.感觉这问题也许有点意思,所以在下面简述一下. 在 ...

  9. Maven - settings.xml里的offline节点的作用

    场景 某天我在本地修改了某个子项目的代码,并进行了打包:mvn clean install -DskipTests,接着我运行父项目却发现自己刚刚的改动并没有生效,或者说,我刚刚打包好的子项目变回了打 ...

最新文章

  1. 【CodeVS】p1299 切水果
  2. windowsphone开发_APP软件开发用哪些软件比较好
  3. 深度学习100例-卷积神经网络(CNN)彩色图片分类 | 第2天
  4. NSMutableArray
  5. 再等等!iPhone 11和iPhone SE还会继续降价
  6. 孙子和外孙就是不一样吗?
  7. Flutter布局锦囊---验证码倒计时
  8. LC-130 被环绕区域
  9. QQ空间自动点赞评论助手 微博自动点赞自动转发神器带源码(超简单)
  10. 从0到1搭建大数据平台之开篇
  11. 量子十问之四:“薛定谔猫”为什么会自然死亡?
  12. 汉诺塔自动解题动画中的iOS开发技巧
  13. Powershell 数字转大写中文,数字金额转中文繁体(改自C#)
  14. C语言switch语句用法总结
  15. LWN: 名为 Sequoia 的 seq_file 漏洞!
  16. 软件测试中的杀虫剂效应与金字塔模型
  17. yolov3中的route和shortcut层
  18. 托福 独立写作题型分类与总结
  19. MIMIC数据库基本信息分析
  20. [OpenGL] 屏幕后处理:景深效果

热门文章

  1. 计算机考试模拟系统无法进入,全国计算机等级考试上机考试模拟系统使用说明...
  2. Service Mesh简史
  3. java的反射机制的好处
  4. Kubernetes 1.5安装
  5. CentOS启动时报错修复
  6. 学习clojure(2)
  7. Java团队+.NET团队==足球队+篮球队;我们能否同时培养好2个优秀的精英团队?
  8. IDEA使用lombok时warn:Generating equals/hashCode implementation but without a call to superclass
  9. 138. 复制带随机指针的链表
  10. ios java 通信_AES加密解密 -- iOS、Java之间的互相通信