入坑写代码2——C#解析xml文件续集
Posted 马克布克打字机
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了入坑写代码2——C#解析xml文件续集相关的知识,希望对你有一定的参考价值。
第一次写解析xml文件算是顺利搞定,没想到后来陆续提出了更多的需求,主要是遇到以下几个问题:
load整个文件。
代码专用了。
存在多个同名元素。
对于问题1,load整个文件这个问题。既然不能将上G的文件一次性load进内存,那就改用流读取(stream)的形式。在网上找到一个不错的读取函数,IEnumerable<XElement> StreamXElements(string uri, string matchname),输入xmlFileUri,以及需要匹配的元素的名字matchname,即可以IEnumerable的形式返回,这样写代码时可以foreach了。StreamXElements的具体代码如下:
1static IEnumerable<XElement> StreamXElements(string uri, string matchname)
2 {
3 XmlReaderSettings settings = new XmlReaderSettings();
4 settings.IgnoreComments = true;
5 settings.IgnoreWhitespace = true;
6
7 using (XmlReader reader = XmlReader.Create(uri, settings))
8 {
9 reader.MoveToContent();
10 while (!reader.EOF)
11 {
12 if (reader.NodeType == XmlNodeType.Element
13 && reader.Name == matchname)
14 {
15 XElement el = XElement.ReadFrom(reader) as XElement;
16 if (el != null)
17 {
18 yield return el;
19 }
20 }
21 else
22 {
23 reader.Read();
24 }
25 }
26 }
27 }
对于问题2,代码专用,因为后边陆续查看了几个xml的Schema,发现什么深度的都有,所以考虑采用递归的形式去解决这块问题,即递归不断的去下钻子代,直到边界条件:1、无子代;或2、子代为item元素。递归这块的伪代码是:
1tatic Dictionary<string, string> getDic(输入的eleArg,输入的key)
2 {
3 if (eleArg有子代)
4 {
5 if (eleArg的子代是不是item)
6 {
7 // 有子代,且是item,
8 // 则按照处理item的方式做处理,返回dic
9 }
10 else
11 {
12 // 有子代,但不是item
13 // 则继续下钻,下钻时,将不断延伸的key传递下去
14 foreach (遍历eleArg的每一个子代eleSon)
15 {
16 // MergeDic,合并字典用的
17 dic = MergeDic(dic, getDic(eleSon, key+=eleArg的name);
18 }
19 }
20 }
21 else
22 {
23 // 没有子代,到头了。
24 // 则按照处理末端元素的方式,返回dic
25 }
26 return dic;
27 }
具体代码为:
1static Dictionary<string, string> getDic(XElement ElementArg, string keyArg)
2 {
3 Dictionary<string, string> dic = new Dictionary<string, string>();
4
5 if (ElementArg.HasElements)
6 {
7 XElement ele = ElementArg.FirstNode as XElement;
8 if (ele.Name.ToString().Equals("item"))
9 {
10 // 有子代,且是item,则对ElementArg的子代开做处理,返回dic
11 IEnumerable<XElement> items = from temp in ElementArg.Elements("item")
12 select temp;
13 foreach (var item in items)
14 {
15 string strKey = keyArg + ElementArg.Name.ToString() + "_" + item.Element("name").Value.ToString() + "_";
16 string strValue = item.Element("value").Value.ToString();
17 dic.Add(strKey, strValue);
18 }
19 return dic;
20 }
21 else
22 {
23 // 有子代,但不是item,则继续下钻。下钻时,将不断延伸的key传递下去
24 IEnumerable<XElement> items = ElementArg.Elements();
25 foreach (var item in items)
26 {
27 dic = MergeDic(dic, getDic(item, keyArg + ElementArg.Name.ToString() + "_"));
28 }
29 }
30 }
31 else
32 {
33 // 没有子代,到头了。则返回 name-value
34 string strKey = keyArg + ElementArg.Name.ToString() + "_";
35 string strValue = ElementArg.Value.ToString();
36 dic.Add(strKey, strValue);
37 }
38
39 return dic;
40 }
对于问题3,同级别下存在同名元素。由于解决第二个问题时已采用递归的方式,那么同级别下的同名元素,最可能出现的地方,就是对同级别的每一个元素做下钻并返回值做字典合并时(上述伪代码中的MergeDic)会遇到。这里直接采用字段后添加序号的形式来处理,举例:某字段key为AAA,mergeDic遇到时,则新增字段为AAA_1,还有下一个,则为AAA_2。所以这里在MergeDic中加了个while循环判断containsKey,若该序号已存在,则往上累加。MergeDic,和GetSequenceNextCount的具体代码为:
1static Dictionary<string, string> MergeDic(Dictionary<string, string> dic1, Dictionary<string, string> dic2)
2 {
3 Dictionary<string, string> dicRes = new Dictionary<string, string>(dic1);
4 foreach (string key in dic2.Keys)
5 {
6 string newKey = key;
7 while (dicRes.ContainsKey(newKey))
8 {
9 newKey = GetSequenceNextCount(newKey);
10 }
11 dicRes.Add(newKey, dic2[key]);
12 }
13
14 return dicRes;
15 }
16
17private static string GetSequenceNextCount(string str)
18 {
19 string res;
20 int count;
21
22 Regex r = new Regex("\\d+$");
23 var ms = r.Matches(str);
24 if (ms.Count > 0)
25 {
26 int.TryParse(
27 ms.OfType<Match>().Last().ToString(),
28 out count);
29 int nextCount = count + 1;
30 res = str.Substring(0, str.Length - count.ToString().Length) + nextCount.ToString();
31 }
32 else
33 {
34 res = str + "1";
35 }
36
37 return res;
38 }
解决三个问题后,解析xml文件的主体就只剩下几行了(见下边代码块)。比如还是处理Topological.xml文件,对每一个Topological元素,只需一句dic = getDic(MatchEle, "")即可。
1static void Main(string[] args)
2 {
3 string strFileUri = @"D:\aaa.xml";
4 string strMatchname = "TrailNtwProtection";
5 IEnumerable<XElement> MatchElements = StreamXElements(strFileUri, strMatchname);
6
7 Dictionary<string, string> dic = new Dictionary<string, string>();
8 foreach (var MatchEle in MatchElements)
9 {
10 dic = getDic(MatchEle, "");
11 // do sth with dic
12 dic.Clear();
13 }
14 }
比如TopologicalLink.xml文件:
比如TrailNtwProtection.xml文件:
就这样,不管采集平台那边来什么xml文件都可以解析了。下一步就看看窗口界面怎么写了,定时更新、多个文件的勾选、单击运行、FTP的文件目录、下载、解压缩。
以上是关于入坑写代码2——C#解析xml文件续集的主要内容,如果未能解决你的问题,请参考以下文章