背景
这几天手上有个活,解析xml,众所周知xml的解析方法有:
- DOM
- SAX
- linq to xml
- plinq
测试用xml和生成代码
1 static void CreateFile() 2 { 3 int N = 5000000; 4 Random rand = new Random(); 5 using (var writer = new XmlTextWriter("VeryHugeXmlFile.xml", Encoding.UTF8)) 6 { 7 writer.Formatting = Formatting.Indented; 8 9 writer.WriteStartDocument(); 10 writer.WriteStartElement("Root"); 11 for (int count = 1; count <= N; count++) 12 { 13 writer.WriteStartElement("Person"); 14 writer.WriteElementString("Id", count.ToString()); 15 writer.WriteElementString("Name", rand.Next().ToString()); 16 writer.WriteElementString("Sex", rand.Next(0, 2) == 0 ? "男" : "女"); 17 writer.WriteElementString("Age", rand.Next(1, 101).ToString()); 18 writer.WriteEndElement(); 19 } 20 writer.WriteEndElement(); 21 writer.WriteEndDocument(); 22 } 23 }
之后会生成类似于下面的xml文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <Root> 3 <Person> 4 <Id>1</Id> 5 <Name>897639886</Name> 6 <Sex>女</Sex> 7 <Age>80</Age> 8 </Person> 9 <Person> 10 <Id>2</Id> 11 <Name>2012162696</Name> 12 <Sex>女</Sex> 13 <Age>60</Age> 14 </Person> 15 <Person>
测试代码
统计时间(只是粗略统计了一下运行时间)
1 static void Watch(Action<string> way, string file) 2 { 3 Stopwatch watch = new Stopwatch(); 4 5 watch.Start(); 6 way(file); 7 watch.Stop(); 8 Console.WriteLine(watch.ElapsedMilliseconds); 9 }
DOM
1 static void DomWay(string file) 2 { 3 XmlDocument doc = new XmlDocument(); 4 doc.Load(file); 5 6 Console.WriteLine(doc.SelectNodes(YOUR-XPATH-HERE).Count); 7 8 }
SAX
1 static void SaxWay(string file) 2 { 3 using (XmlTextReader reader = new XmlTextReader(file)) 4 { 5 int count = 0; 6 while (reader.Read()) 7 { 8 if (reader.Name == "Person" && reader.NodeType == XmlNodeType.Element) 9 { 10 reader.Read(); 11 reader.Read(); 12 13 int? Id = null; 14 int? name = null; 15 string sex = null; 16 int? age = null; 17 18 if (reader.Name == "Id") 19 { 20 Id = reader.ReadElementContentAsInt(); 21 reader.Read(); 22 name = reader.ReadElementContentAsInt(); 23 reader.Read(); 24 sex = reader.ReadElementContentAsString(); 25 reader.Read(); 26 age = reader.ReadElementContentAsInt(); 27 reader.Read(); 28 } 29 30 if (reader.Name == "Person" && reader.NodeType == XmlNodeType.EndElement) 31 reader.Read(); 32 33 if (Id != null && name != null && sex != null && age != null) 34 { 35 if (在此设置自定义过滤条件) 36 count++; 37 } 38 } 39 } 40 41 Console.WriteLine(count); 42 } 43 }
Linq to Xml
1 static void LinqWay(string file) 2 { 3 var root = XElement.Load(file); 4 var person = from p in root.Elements("Person")
7 where 在此设置自定义过滤条件
8 select id; 9 Console.WriteLine(person.Count()); 10 }
PLinq to Xml
1 static void PLinqWay(string file) 2 { 3 var root = XElement.Load(file); 4 var person = from p in root.Elements("Person").AsParallel()
7 where 在此设置自定义过滤条件
8 select id; 9 Console.WriteLine(person.Count()); 10 }
统计结果
在6核8G内存机器上,测试程序设置为x64和release模式,在xml查询结果相同的情况下取运行时间(ms),没有详细采集cpu和内存数据
两个模式,区别是加了一个素数的判断。
Id > 5000 && sex == "男" && age > 15 && age < 50 |
Id > 5000 && sex == "男" && age > 15 && age < 50 && IsPrimeInt(name) |
|
sax | 13857 | 40010 |
linq | 27336 | 53760 |
plinq | 24550 | 28846 |
dom | 31737 | 0 |
由于dom模式本身xpath模式不支持嵌入函数,所以第二个测试没有采集结果。
小结
sax:速度优先,内存占用少,但是代码复杂度高。
linq:速度较sax慢,但是代码优雅,维护容易
plinq:同上,在非计算密集型模式中,不比linq和sax模式好多少。但是在计算密集下,后来居上
dom:速度落后,但是原生支持xpath,代码最优雅。
内存方面仅是肉眼观察了任务管理器,sax基本内存曲线为水平线,而linq&plinq在load的时候分配内存,可能其内部也是用了dom。
仓促行文,其中必有不实之处,往各位劳神指教。