博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
在xml里增加节点时添加回车(libxml2)
阅读量:4055 次
发布时间:2019-05-25

本文共 6237 字,大约阅读时间需要 20 分钟。


上一个
关于libxml2的一篇文章中
 ,添加的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,都无法实现
在新追加的结点后面添加回车换行。
在的官方网站的maillist里关于这方面的信息非常少。但是,对我帮助最大还是
 这个问题里的例子代码,里面在设置
属性的时候用的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    }

运行该段代码,有时候会在使用的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()的另一部分代码里,删除节点的程序去不是这样做。

例如,在一个结点被删除后,通常会使用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://rpoci.baihongyu.com/

你可能感兴趣的文章
机器学习实战之决策树二
查看>>
[LeetCode By Python]7 Reverse Integer
查看>>
[LeetCode By Python]121. Best Time to Buy and Sell Stock
查看>>
[LeetCode By Python]136. Single Number
查看>>
Android/Linux 内存监视
查看>>
Android2.1消息应用(Messaging)源码学习笔记
查看>>
iphone开发之SDK研究(待续)
查看>>
计算机网络复习要点
查看>>
Variable property attributes or Modifiers in iOS
查看>>
NSNotificationCenter 用法总结
查看>>
C primer plus 基础总结(一)
查看>>
剑指offer算法题分析与整理(三)
查看>>
mint/ubuntu安装搜狗输入法
查看>>
C++动态申请数组和参数传递问题
查看>>
opencv学习——在MFC中读取和显示图像
查看>>
JVM并发机制探讨—内存模型、内存可见性和指令重排序
查看>>
nginx+tomcat+memcached (msm)实现 session同步复制
查看>>
c++模板与泛型编程
查看>>
WAV文件解析
查看>>
WPF中PATH使用AI导出SVG的方法
查看>>