0

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?

4

3 回答 3

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);
        }
    }
}
于 2013-09-28T14:48:47.133 回答
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<> 具有扩展方法“.ToArray()”和“.ToList()”。

于 2013-09-28T16:14:01.810 回答
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 的类一团糟。

你创建你的系统数组,就像我在上面创建监视器的数组一样:

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

现在,您在易于使用的类中拥有了所有的价值。

于 2013-09-28T18:32:27.953 回答