如何在列中选择行?

时间:2013-09-28 14:11:08

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

Xml如下。

<System>
  <ID></ID>
  <Name></Name>
  <Monitor>
    <ID></ID>
    <Type></Type>
    <Alert>
      <ID></ID>
      <Status></Status>
    </Alert>
    <Alert>
      <ID></ID>
      <Status></Status>
    </Alert>
  </Monitor>
</System>
<System>
  <ID></ID>
  <Name></Name>
  <Monitor>
    <ID></ID>
    <Type></Type>
    <Alert>
      <ID></ID>
      <Status></Status>
    </Alert>
  </Monitor>
</System>

我想像这样遍历它

XElement xmlDoc = XElement.Load(@"xml");
var q = from el in xmlDoc.Elements("System") select el;

foreach(el in q) {
    Console.WriteLine(el.ID);
    Console.WriteLine(el.Name);

    if (el.Monitor) {
        foreach (mon in el.Monitor) {
            Console.WriteLine(el.ID);
            Console.WriteLine(el.Type);

            if (mon.Alert) {
                foreach (alert in mon.Alert) {
                    Console.WriteLine(alert.ID);
                    Console.WriteLine(alert.Status);
                }
            }
        }
    }
}

目前我循环遍历了几次并使用if检查字段然后为变量赋值。然后我必须再次循环它。有没有更简单的方法,我应该使用简单的LINQ或LINQ-TO-XML吗?

3 个答案:

答案 0 :(得分:0)

如果你想像你那样遍历它:

var xml = @"
<Root>
<System>
<ID>1</ID>
<Name>One</Name>
<Monitor>
    <ID>2</ID>
    <Type>Two</Type>
    <Alert>
    <ID>3</ID>
    <Status>Three</Status>
    </Alert>
    <Alert>
    <ID>4</ID>
    <Status>Four</Status>
    </Alert>
</Monitor>
</System>
<System>
<ID>5</ID>
<Name>Five</Name>
<Monitor>
    <ID>6</ID>
    <Type>Six</Type>
    <Alert>
    <ID>7</ID>
    <Status>Seven</Status>
    </Alert>
</Monitor>
</System>
</Root>
";

XElement xmlDoc = XElement.Parse(xml);
var q = xmlDoc.Elements("System");

foreach(var el in q) {
    Console.WriteLine(el.Element("ID").Value);
    Console.WriteLine(el.Element("Name").Value);

    foreach(var mon in el.Elements("Monitor")) {
        Console.WriteLine(mon.Element("ID").Value);
        Console.WriteLine(mon.Element("Type").Value);

        foreach(var alert in mon.Elements("Alert")) {
            Console.WriteLine(alert.Element("ID").Value);
            Console.WriteLine(alert.Element("Status").Value);
        }
    }
}

答案 1 :(得分:0)

好的,试试这段代码(见下面的代码解释)

    class Program {
        static void Main(string[] args) {
            var xml = @"
<Root>
  <System>
    <ID>1</ID>
    <Name>one</Name>
    <Monitor>
      <ID>3</ID>
      <Type>t3</Type>
      <Alert>
        <ID>5</ID>
        <Status>a5</Status>
      </Alert>
      <Alert>
        <ID>6</ID>
        <Status>a6</Status>
      </Alert>
    </Monitor>
  </System>
  <System>
    <ID>2</ID>
    <Name>two</Name>
    <Monitor>
      <ID>4</ID>
      <Type>t4</Type>
      <Alert>
        <ID>7</ID>
        <Status>a7</Status>
      </Alert>
    </Monitor>
  </System>
</Root>
";
            XElement xmlDoc = XElement.Parse(xml);
            // set q to an enumeration of XElements
            // where the elements xname is "System"
            // the query actually executes the first time q is used
            var q = xmlDoc.Elements("System");
            foreach (var ele in q) {
                // Get the value of the Element with the xname of "ID"
                Console.WriteLine(ele.Element("ID").Value); 
                Console.WriteLine(ele.Element("Name").Value);
                // if ele.Elements("Monitor") returns nothing
                // then the foreach will be skipped (null-execution)
                foreach (var mon in ele.Elements("Monitor")) {
                    Console.WriteLine(mon.Element("ID").Value);
                    Console.WriteLine(mon.Element("Type").Value);
                    foreach (var alert in mon.Elements("Alert")) {
                        Console.WriteLine(alert.Element("ID").Value);
                        Console.WriteLine(alert.Element("Status").Value);
                        }
                    }
                }
            }
        }

此代码将在XML文档中移动一次。在C#中,LINQ包含语言元素(如'select'和'from')和库元素(.NET框架方法,如XDocument.Elements);混合两者是可以的,但只应该了解语句背后发生的事情。在这种情况下,您要求XDocument返回XName为“System”的所有子元素。在上面的代码中,'q'没有接收到所有元素,它接收一个可以迭代的枚举。 q的分配是一个非常低成本的操作,因为XDocument内容在第一个foreach之前不是横向的,然后一次只检查一个元素。搜索“C#yield return”以查看其实现方式。

如果您只对“警报”元素感兴趣,可以执行以下操作:

var alerts = xmlDoc.Descendants("Alert")

这将返回XName为“Alert”的所有元素的枚举(无论它们在XML文档的层次结构中的何处)。如果您想确保层次结构,可以使用“where”,例如:

var alerts = xmlDoc.Descendants("Alert")
                   .Where(ele => (ele.Parent != null) && (ele.Parent.Name == "Monitor"))
                   .Where(ele => (ele.Parent.Parent != null) && (ele.Parent.Parent.Name == "System"));
foreach (var alert in alerts) {
    Console.WriteLine(alert.Element("ID").Value);
    Console.WriteLine(alert.Element("Status").Value);
    }

如果需要多次迭代相同的节点,则应考虑将枚举转换为列表或数组,这样可以节省时间,但会增加内存使用量。 IEnumerable的&LT;&GT;为此目的具有扩展方法“。ToArray()”和“.ToList()”。

答案 2 :(得分:0)

C#是一种OOP语言,我认为你应该利用它:

示例:

public class MySystem
{
    public int Id { get; private set; }
    public string Name { get; private set; }

    public MyMonitor[] Monitors { get; private set; }

    public MySystem(XElement x)
    {
        Id = (int)x.Element("ID");
        Name = x.Element("Name").Value;
        // a little confusing from your code if there can be more than one Monitor
        Monitors = x.Elements("Monitor").Select(m => new MyMonitor(m)).ToArray();
    }
}

为您的Monitor类和Alert类执行类似的操作。我把它命名为MySystem,因为名为System的类是一团糟。

您可以创建系统数组,例如我使用以下方法创建了Monitor的数组:

XElement xmlDoc = XElement.Load(@"xml");
MySystem[] systems = xmlDoc.Elements("System")
                           .Select(s => new MySystem(s))
                           .ToArray();

现在,您已将所有值都放在易于使用的类中。