C# 递归解析xml文件

 第一次写解析xml文件算是顺利搞定,没想到后来陆续提出了更多的需求,主要是遇到以下几个问题:

  1. load整个文件。

  2. 代码专用了。

  3. 存在多个同名元素。

        



        对于问题1,load整个文件这个问题。既然不能将上G的文件一次性load进内存,那就改用流读取(stream)的形式。在网上找到一个不错的读取函数,IEnumerable<XElement> StreamXElements(string uri, string matchname),输入xmlFileUri,以及需要匹配的元素的名字matchname,即可以IEnumerable的形式返回,这样写代码时可以foreach了。StreamXElements的具体代码如下:

static IEnumerable<XElement> StreamXElements(string uri, string matchname)
        {
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreComments = true;
            settings.IgnoreWhitespace = true;

            using (XmlReader reader = XmlReader.Create(uri, settings))
            {
                reader.MoveToContent();
                while (!reader.EOF)
                {
                    if (reader.NodeType == XmlNodeType.Element
                        && reader.Name == matchname)
                    {
                        XElement el = XElement.ReadFrom(reader) as XElement;
                        if (el != null)
                        {
                            yield return el;
                        }
                    }
                    else
                    {
                        reader.Read();
                    }
                }
            }
        }

       对于问题2,代码专用,因为后边陆续查看了几个xml的Schema,发现什么深度的都有,所以考虑采用递归的形式去解决这块问题,即递归不断的去下钻子代,直到边界条件:1、无子代;或2、子代为item元素。递归这块的伪代码是:

static Dictionary<string, string> getDic(输入的eleArg,输入的key)
{
    if (eleArg有子代)
    {
        if (eleArg的子代是不是item)
        {
            // 有子代,且是item,
            // 则按照处理item的方式做处理,返回dic
        }
        else
        {
            // 有子代,但不是item
            // 则继续下钻,下钻时,将不断延伸的key传递下去
            foreach (遍历eleArg的每一个子代eleSon)
            {
                // MergeDic,合并字典用的
                dic = MergeDic(dic, getDic(eleSon, key+=eleArg的name);
            }
        }
    }
    else
    {
        // 没有子代,到头了。
        // 则按照处理末端元素的方式,返回dic
    }
    return dic;
}

        具体代码为:

static Dictionary<string, string> getDic(XElement ElementArg, string keyArg)
        {
            Dictionary<string, string> dic = new Dictionary<string, string>();
             if (ElementArg.HasElements)
            {
                XElement ele = ElementArg.FirstNode as XElement;
                if (ele.Name.ToString().Equals("item"))
                {
                    // 有子代,且是item,则对ElementArg的子代开做处理,返回dic
                    IEnumerable<XElement> items = from temp in ElementArg.Elements("item")
                                                  select temp;
                    foreach (var item in items)
                    {
                        string strKey = keyArg + ElementArg.Name.ToString() + "_" + item.Element("name").Value.ToString() + "_";
                        string strValue = item.Element("value").Value.ToString();
                        dic.Add(strKey, strValue);
                    }
                    return dic;
                }
              else
              {
                  // 有子代,但不是item,则继续下钻。下钻时,将不断延伸的key传递下去
                  IEnumerable<XElement> items = ElementArg.Elements();
                  foreach (var item in items)
                  {
                       dic = MergeDic(dic, getDic(item, keyArg + ElementArg.Name.ToString() + "_"));
                  }
                }
              }
            else
            {
                // 没有子代,到头了。则返回 name-value
                string strKey = keyArg + ElementArg.Name.ToString() + "_";
                string strValue = ElementArg.Value.ToString();
                dic.Add(strKey, strValue);
            }

            return dic;
       }

        对于问题3,同级别下存在同名元素。由于解决第二个问题时已采用递归的方式,那么同级别下的同名元素,最可能出现的地方,就是对同级别的每一个元素做下钻并返回值做字典合并时(上述伪代码中的MergeDic)会遇到。这里直接采用字段后添加序号的形式来处理,举例:某字段key为AAA,mergeDic遇到时,则新增字段为AAA_1,还有下一个,则为AAA_2。所以这里在MergeDic中加了个while循环判断containsKey,若该序号已存在,则往上累加。MergeDic,和GetSequenceNextCount的具体代码为:

static Dictionary<string, string> MergeDic(Dictionary<string, string> dic1, Dictionary<string, string> dic2)
        {
            Dictionary<string, string> dicRes = new Dictionary<string, string>(dic1);
            foreach (string key in dic2.Keys)
            {
                string newKey = key;
                while (dicRes.ContainsKey(newKey))
                {
                    newKey = GetSequenceNextCount(newKey);
                }
                dicRes.Add(newKey, dic2[key]);
            }

            return dicRes;
        }

private static string GetSequenceNextCount(string str)
        {
            string res;
            int count;

            Regex r = new Regex("\\d+$");
            var ms = r.Matches(str);
            if (ms.Count > 0)
            {
                int.TryParse(
                    ms.OfType<Match>().Last().ToString(), 
                    out  count);
                int nextCount = count + 1;
                res = str.Substring(0, str.Length - count.ToString().Length) + nextCount.ToString();
            }
            else
            {
                res = str + "1";
            }

            return res;
        }

        解决三个问题后,解析xml文件的主体就只剩下几行了(见下边代码块)。比如还是处理Topological.xml文件,对每一个Topological元素,只需一句dic = getDic(MatchEle, "")即可。

static void Main(string[] args)
        {
            string strFileUri = @"D:\aaa.xml";
            string strMatchname = "TrailNtwProtection";
            IEnumerable<XElement> MatchElements = StreamXElements(strFileUri, strMatchname);

            Dictionary<string, string> dic = new Dictionary<string, string>();
            foreach (var MatchEle in MatchElements)
            {
                dic = getDic(MatchEle, "");
                // do sth with dic 
                dic.Clear();
            }
        }

 比如TopologicalLink.xml文件:

 比如TrailNtwProtection.xml文件:


       就这样,不管采集平台那边来什么xml文件都可以解析了。下一步就看看窗口界面怎么写了,定时更新、多个文件的勾选、单击运行、FTP的文件目录、下载、解压缩。

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/muxiong0308/article/details/79946062
文章标签: C# XML 递归
个人分类: 字符串处理
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭
关闭