xmlreader与xmlwriter里的几个坑与解决方案
加载超过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
这是用stringbuilder才会有的问题,改用filestream、memorystream等就好了。
2.(utf8)改用memorystream后,形成的xml字符串通过xmldocument.loadxml时报错
xmlwritersettings settings = new xmlwritersettings();
settings.encoding = encoding.utf8;
最终发现默认的encoding.utf8是带有字节顺序标记的,要用new utf8encoding(false);
通过监视区代码可以看到,xmlstr[0]是65279,修改后就对了变成60'<'。
3.xmlreader默认不读取内容中的回车换行,读进来就是个空格。
第二个直接回车换行就是读不进来,用xmldocument可以读到两个,xmlreader就是读取不到。
期间一直在找设置,比如ignorewhitespace等,发现都没有用,还是不读。
xmlreadersettings rsettings = new xmlreadersettings();
rsettings.ignorewhitespace = false;
最后在*上找到答案(注1),不能用xmlreader rdr = xmlreader.create(path),用xmltextreader就好了。
注1:不读回车换行问题
this is because the xmltextreader has a normalization setting defaulted to false unlike xmlreader.create which always normalizes newlines no matter what.