欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

xmlreader与xmlwriter里的几个坑与解决方案

程序员文章站 2022-12-15 17:57:01
加载超过100M的xml文件时(可能不是很常见),XmlDocument这种全部加载到内存里的模式就有点不友好了,耗时长、内存高。 这时用xmlreader就会有自行车换超跑的感觉,但其间遇到几个坑,记录一下。 先看源码,包括dom和sax两种模式的读取和写入 DOM模式: SAX(simple A ......

 

加载超过100m的xml文件时(可能不是很常见),xmldocument这种全部加载到内存里的模式就有点不友好了,耗时长、内存高。

这时用xmlreader就会有自行车换超跑的感觉,但其间遇到几个坑,记录一下。

先看源码,包括dom和sax两种模式的读取和写入

dom模式:

 1         /// <summary>
 2         /// dom模式创建xml文件
 3         /// </summary>
 4         /// <param name="path"></param>
 5         public void createxml_dom(string path)
 6         {
 7             xmldocument xmldocw = new xmldocument();
 8             //xml头
 9             var xmldecl = xmldocw.createxmldeclaration("1.0", "utf-8", null);
10             var root = xmldocw.createelement("root");
11             root.setattribute("name", "李四");
12             var test = xmldocw.createelement("test");
13             root.appendchild(test);
14 
15             xmldocw.appendchild(xmldecl);
16             xmldocw.appendchild(root);
17             xmldocw.save(path);
18 
19             //可以通过xmlreader读数据后生成节点
20             //var node = xmldocw.readnode(rdr);
21             //root.appendchild(node);
22             //或者读取outerxml后作为innerxml写入
23             //string str = rdr.readouterxml();
24             //root.innerxml = str;
25         }
26 
27         /// <summary>
28         /// dom模式读取xml
29         /// </summary>
30         /// <param name="path"></param>
31         public void readxml_dom(string path)
32         {
33             xmldocument xmldocr = new xmldocument();
34             xmldocr.load(path);
35             var root = xmldocr.documentelement;
36             string str = root.getattribute("name");
37             console.writeline(str);
38         }

 

sax(simple api for xml)模式:几种错误也都用注释标注出来了

 1         /// <summary>
 2         /// xmlwriter创建xml文件
 3         /// </summary>
 4         /// <param name="path"></param>
 5         public void createxml_sax(string path)
 6         {
 7             //filestream没问题
 8             //filestream stream = new filestream(path,filemode.create);
 9             //会出现编码一直是utf-16问题
10             //stringbuilder stream = new stringbuilder();
11             memorystream stream = new memorystream();
12             xmlwritersettings settings = new xmlwritersettings();
13             //encoding.utf8这个会报错,字节顺序标记
14             settings.encoding = new utf8encoding(false);
15             xmlwriter xw = xmlwriter.create(stream, settings);
16             //xmltextwriter xw = new xmltextwriter(stream, new utf8encoding(false)); 
17 
18             //写入声明
19             xw.writestartdocument();
20 
21             xw.writestartelement("root");
22             xw.writeattributestring("name", "张三");
23             //可以通过xmlreader读数据后直接写入
24             //xw.writenode(rdr);
25             xw.writestartelement("test");
26             xw.writeendelement();
27 
28             xw.writeendelement();
29 
30             xw.writeenddocument();
31             xw.close();
32 
33             string xmlstr = encoding.utf8.getstring(stream.toarray());
34             stream.close();
35             xmldocument xmldocw = new xmldocument();
36             xmldocw.loadxml(xmlstr);
37             xmldocw.save(path);
38         }
39 
40         /// <summary>
41         /// xmlreader读取xml
42         /// </summary>
43         /// <param name="path"></param>
44         public void readxml_sax(string path)
45         {
46             xmldocument xmldocw = new xmldocument();
47             xmlreadersettings rsettings = new xmlreadersettings();
48             rsettings.ignorecomments = true;
49             rsettings.ignorewhitespace = false;
50             rsettings.checkcharacters = false;
51             //默认的xmlreader不读取内容中的回车换行\r\n
52             //(xmlreader rdr = xmlreader.create(path,rsettings))
53             using (xmltextreader rdr = new xmltextreader(path))
54             {
55                 rdr.whitespacehandling = whitespacehandling.significant;
56                 string elename = "";                
57                 while (rdr.read())
58                 {
59                     if (rdr.nodetype == xmlnodetype.element)
60                     {
61                         //节点名称
62                         elename = rdr.name;
63                         //节点深度
64                         int dp = rdr.depth;
65                         //是否空节点,表示<elememt/> 不是<element></element>
66                         bool needend = rdr.isemptyelement;
67                         for (int i = 0; i < rdr.attributecount; i++)
68                         {
69                             rdr.movetoattribute(i);
70                             console.writeline(rdr.name+":"+rdr.value);
71                         }
72                         //可以直接读取节点所有的数据.可以用readnode读取
73                         //rdr.eof判定,不然会跳过节点
74                         //rdr.readouterxml();
75                     }
76                     else if (rdr.nodetype == xmlnodetype.endelement)
77                     {
78                         elename = rdr.name;
79                     }
80                 }
81             }
82         }

xmlreader和xmldocument(xmlwriter)组合一起用对大型xml进行拆分读取,十分有效。 

 

下面是遇到的问题:

1.xmlwriter后xml文件头始终是utf-16

xmlreader与xmlwriter里的几个坑与解决方案

这是用stringbuilder才会有的问题,改用filestream、memorystream等就好了。

 

2.(utf8)改用memorystream后,形成的xml字符串通过xmldocument.loadxml时报错

xmlwritersettings settings = new xmlwritersettings();
settings.encoding = encoding.utf8;

xmlreader与xmlwriter里的几个坑与解决方案

最终发现默认的encoding.utf8是带有字节顺序标记的,要用new utf8encoding(false);

通过监视区代码可以看到,xmlstr[0]是65279,修改后就对了变成60'<'。

 xmlreader与xmlwriter里的几个坑与解决方案

xmlreader与xmlwriter里的几个坑与解决方案


3.xmlreader默认不读取内容中的回车换行,读进来就是个空格。

xmlreader与xmlwriter里的几个坑与解决方案

第二个直接回车换行就是读不进来,用xmldocument可以读到两个,xmlreader就是读取不到。

 xmlreader与xmlwriter里的几个坑与解决方案

   期间一直在找设置,比如ignorewhitespace等,发现都没有用,还是不读。

 xmlreadersettings rsettings = new xmlreadersettings();
    rsettings.ignorewhitespace = false;

 最后在*上找到答案(注1),不能用xmlreader rdr = xmlreader.create(path),用xmltextreader就好了。

 xmlreader与xmlwriter里的几个坑与解决方案

 

 

注1:不读回车换行问题

this is because the xmltextreader has a normalization setting defaulted to false unlike xmlreader.create which always normalizes newlines no matter what.