【问题标题】:C# same level XML ParsingC#同级XML解析
【发布时间】:2012-07-02 07:25:30
【问题描述】:

我得到了这个 XML 文件:

<Msg UserText="start 0">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 1">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="start 2">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>
<Msg UserText="A">
</Msg>

我需要计算每个“开始 x”之间有多少个“A”

即对于上面我会输出:

start 0 : 2 
start 1 : 1 
start 2 : 3

我应该如何在 C# 中解决这个问题?我有几个方向,但我敢肯定那里有更简单的(例如使用 linq)

【问题讨论】:

  • 你有没有尝试过(我的意思是除了在这里发布这个问题)?例如使用 Visual Studio 编写一些代码并将您听到的指示付诸行动?您在执行此操作时是否遇到了一些具体问题?
  • @DarinDimitrov 我做到了,我尝试遍历所有节点,但代码似乎很麻烦,我希望能够深入了解同级别解析。
  • msdn.microsoft.com/en-us/library/bb352694 遍历 Msg 无论您的父元素是什么的后代?
  • @nbrooks 已经这样做了,我希望有一个 select ... 子句可以为我将它们分组,而不是保持我所在的状态。

标签: c# xml-parsing linq-to-xml


【解决方案1】:

这是一个分组示例:

XDocument doc = XDocument.Load("../../XMLFile1.xml");

var groups = from msg in doc.Root.Elements("Msg")
             where !((string)msg.Attribute("UserText")).StartsWith("start")
             group msg by 
             msg.ElementsBeforeSelf("Msg").Where(m => 
                 ((string)m.Attribute("UserText")).StartsWith("start")).Last();

foreach (var group in groups)
{
    Console.WriteLine("Group starting with {0} has {1} member(s).", 
        group.Key.Attribute("UserText"), group.Count());
}

使用 XML 输入示例 XMLFile1.xml

<Root>
  <Msg UserText="start 0">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="start 1">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="start 2">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
  <Msg UserText="A">
  </Msg>
</Root>

我得到了输出

Group starting with UserText="start 0" has 2 member(s).
Group starting with UserText="start 1" has 1 member(s).
Group starting with UserText="start 2" has 3 member(s).

【讨论】:

  • 正确,但由于在ElementsBeforeSelf 和相应的子查询中进行反向搜索,XML 速度较慢。
  • 谢谢,上面的代码真的帮助我理解了如何使用Linq-to-Xml!!
【解决方案2】:

利用ElementsAfterSelf() 方法获取以下兄弟。然后从那里很容易:

var root = XElement.Parse(xmlStr);
var query =
    from msg in root.Elements("Msg")
    let message = (string)msg.Attribute("UserText")
    where message.StartsWith("start")
    select new
    {
        Message = message,
        FollowingAs = msg.ElementsAfterSelf("Msg")
            .TakeWhile(e => (string)e.Attribute("UserText") == "A")
            .Count(),
    };

【讨论】:

  • 谢谢!很好的答案,我从中学到了很多。可惜我只能选择一个答案。
【解决方案3】:
    static void Main(string[] args)
    {
         const string xml = @"<SomeRootTag>
            <Msg UserText='start 0'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='start 1'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='start 2'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
            <Msg UserText='A'>
            </Msg>
        </SomeRootTag>";

        var xDoc = XDocument.Load(new StringReader(xml));
        var msgs = xDoc.Root.Elements().Where(el => el.Name == "Msg").Select(el => el.Attribute("UserText").Value);
        var results = GetCounts(msgs);



        foreach (var keyValue in results)
        {
            Console.WriteLine("{0}:{1}", keyValue.Item1, keyValue.Item2);
        }

        Console.ReadKey();
    }

    private static IEnumerable<Tuple<string,int>> GetCounts(IEnumerable<string> msgs)
    {
        string last = null;
        int count = 0;
        foreach (var msg in msgs)
        {
            if (msg.StartsWith("start"))
            {
                if (last != null)
                {
                    yield return new Tuple<string, int>(last, count);
                }
                count = 0;
                last = msg;
            }
            else
            {
                count++;
            }
        }
        yield return new Tuple<string, int>(last, count);
    }  

【讨论】:

    【解决方案4】:

    这应该为你做。

    using System.Collections.Generic;
    using System.Xml.Linq;
    
    namespace TraverseXMLNodes
    {
        public class Class1
        {
            public static void Main(string[] args)
            {
                XDocument doc = XDocument.Load(@"C:\Udvikling\StackOverflow\TraverseXMLNodesSln\TraverseXMLNodes\XMLFile1.xml");
                var msgs = doc.Element("root").Elements("Msg");
    
                List<int> numbers = new List<int>();
                List<string> numbersStr = new List<string>();
                int count = 0;
                foreach (var xElement in msgs)
                {
                    string value = xElement.Attribute("UserText").Value;
                    numbersStr.Add(value);
                    if (value.Contains("start"))
                    {
                        numbers.Add(count);
                        count = 0;
                    }
                    else
                    {
                        count++;
                    }
                }
                numbers.Add(count);
            }
        }
    }
    

    我的 XML 文件:

    <?xml version="1.0" encoding="utf-8" ?>
    <root>
      <Msg UserText="start 0">
      </Msg>
      <Msg UserText="A">
      </Msg>
      <Msg UserText="A">
      </Msg>
      <Msg UserText="start 1">
      </Msg>
      <Msg UserText="A">
      </Msg>
      <Msg UserText="start 2">
      </Msg>
      <Msg UserText="A">
      </Msg>
      <Msg UserText="A">
      </Msg>
      <Msg UserText="A">
      </Msg>
    </root>
    

    【讨论】:

    • C:\Udvikling\StackOverflow - 不错的文件夹。
    • 正确,但没有必要在两个单独的列表中收集所有键和值。调整列表大小时,使用较大的 XML 可能会导致内存浪费和性能下降。
    • 我添加了一些不同的列表,以便他更容易理解输出是什么以及它是如何构建的。我以为他想在以后调试它以学习 som XML 和 XDocument,达到目前适合他的水平。
    • @DarinDimitrov 是的,这是一个必须有的文件夹 :) BTW “Udvikling” 表示英语中的开发
    猜你喜欢
    • 1970-01-01
    • 2019-09-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    • 2015-05-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多