背景
這幾天手上有個活,解析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。
倉促行文,其中必有不實之處,往各位勞神指教。