如何在列中选择行?

时间:2023-02-02 07:19:39

Xml is as follows.

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>

I want to traverse it like this

我想像这样遍历它

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);
                }
            }
        }
    }
}

Currently I loop through each several times and use if to check field and then assign value to a variable. Then I have to loop through it again. Is there an easier way, and should I use plain LINQ or LINQ-TO-XML?

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

3 个解决方案

#1


0  

Ok try this code (see below code for explaination)

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

    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);
                        }
                    }
                }
            }
        }

This code will move through the XML document exactly once. In C# LINQ contains both language elements (like 'select' and 'from') and library element (.NET framework methods like XDocument.Elements); mixing the two is ok, but should only be done with understanding of what is occurring behind the statements. In this case you are asking for the XDocument to return all the child elements with an XName of "System". In the above code 'q' does not receive all of the elements, it receives an enumeration which can be iterated. The assignment of q is a very low cost operation because the XDocument contents are not transverse until the first foreach and then only one element at a time is examined. Do a search for "C# yield return" to see how this is implemented.

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

If you were only interested in the "Alert" elements you could do something like this:

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

var alerts = xmlDoc.Descendants("Alert")

This would return an enumeration of all elements with an XName of "Alert" (regardless of where they are in the hierarchy of the XML document). If you wanted to ensure the hierarchy you can use 'where', for example:

这将返回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);
    }

If you need to iterate over the same nodes multiple times you should consider converting the enumeration to a list or array, this saves time but increases memory usage. IEnumerable<> has extension methods ".ToArray()" and ".ToList()" for this purpose.

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

#2


0  

C# is an OOP language, I think you should harness that for this:

C#是一种OOP语言,我认为你应该利用它来做到这一点:

Example:

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();
    }
}

Do something similar for your Monitor class and your Alert class. I named it MySystem since a class named System is a mess.

为您的Monitor类和Alert类做类似的事情。我把它命名为MySystem,因为名为System的类是一团糟。

You create your array of systems like I created the Monitor's array above with:

你创建了你的系统数组,就像我在上面创建了Monitor的数组:

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

Now you have all your values in easy to use classes.

现在,您可以在易于使用的类中使用所有值。

#3


0  

If you want to traverse it like that you can:

如果你想像那样遍历它:

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  

Ok try this code (see below code for explaination)

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

    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);
                        }
                    }
                }
            }
        }

This code will move through the XML document exactly once. In C# LINQ contains both language elements (like 'select' and 'from') and library element (.NET framework methods like XDocument.Elements); mixing the two is ok, but should only be done with understanding of what is occurring behind the statements. In this case you are asking for the XDocument to return all the child elements with an XName of "System". In the above code 'q' does not receive all of the elements, it receives an enumeration which can be iterated. The assignment of q is a very low cost operation because the XDocument contents are not transverse until the first foreach and then only one element at a time is examined. Do a search for "C# yield return" to see how this is implemented.

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

If you were only interested in the "Alert" elements you could do something like this:

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

var alerts = xmlDoc.Descendants("Alert")

This would return an enumeration of all elements with an XName of "Alert" (regardless of where they are in the hierarchy of the XML document). If you wanted to ensure the hierarchy you can use 'where', for example:

这将返回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);
    }

If you need to iterate over the same nodes multiple times you should consider converting the enumeration to a list or array, this saves time but increases memory usage. IEnumerable<> has extension methods ".ToArray()" and ".ToList()" for this purpose.

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

#2


0  

C# is an OOP language, I think you should harness that for this:

C#是一种OOP语言,我认为你应该利用它来做到这一点:

Example:

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();
    }
}

Do something similar for your Monitor class and your Alert class. I named it MySystem since a class named System is a mess.

为您的Monitor类和Alert类做类似的事情。我把它命名为MySystem,因为名为System的类是一团糟。

You create your array of systems like I created the Monitor's array above with:

你创建了你的系统数组,就像我在上面创建了Monitor的数组:

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

Now you have all your values in easy to use classes.

现在,您可以在易于使用的类中使用所有值。

#3


0  

If you want to traverse it like that you can:

如果你想像那样遍历它:

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);
        }
    }
}