简体   繁体   中英

LINQ and XML with C#

<root>
    <level1 name="A">
        <level2 name="A1" />
        <level2 name="A2" />
    </level1>
    <level1 name="B">
        <level2 name="B1" />
        <level2 name="B2" />
    </level1>
    <level1 name="C" />
</root>

Could someone give me a C# code using LINQ, the simplest way to print this result: (Note the extra space if it is a level2 node)

A
  A1
  A2
B
  B1
  B2
C

XDocument xdoc = XDocument.Load("data.xml"));
var lv1s = from lv1 in xdoc.Descendants("level1")
           select lv1.Attribute("name").Value;

foreach (var lv1 in lv1s)
{
    result.AppendLine(lv1);

    var lv2s = from lv2 in xdoc...???
}
var xDoc = XDocument.Load(filename);
var lookup = xDoc.Descendants("param")
                 .ToLookup(x => x.Attribute("name").Value, x => (double)x);

foreach (var p in lookup)
{
    Console.WriteLine("{0,-12} {1,6:#0.0} {2,6:#0.0} {3,6:#0.0}", 
                        p.Key,  p.Min(),  p.Average(),  p.Max());
}

OUTPUT:

temperature     9.3   10.3   11.2
pH              3.0    6.3   10.0
Phosphate       4.0    4.0    4.0
Chloride        4.0    4.0    4.0
Nitrate        10.0   10.0   10.0

By using XPath, here's how you could do it for the average:

float GetAvgTemperature(XmlDocument document)
{
    var temperatureNodes = document.SelectNodes("/samples/measurement/param[@name='temperature']");
    return temperatureNodes == null ? 0 : temperatureNodes.Cast<XmlNode>().Average(node => float.Parse(node.InnerText, CultureInfo.InvariantCulture));
}

You can easily make this function more generic to return either Minimum/Average/Maximum and for any parameter name.

This approach uses the LINQ aggregation to build the calculations in a single line of code as you requested.

var values = samples.Descendants(XName.Get("param"))
    .Select(n => new {
        Name = n.Attribute(XName.Get("name")).Value,
        Value = float.Parse(n.Value)
    })
    .GroupBy(a => a.Name)
    .Select(a => new {
        Name = a.Key,
        MaxValue = a.Max(g => g.Value),
        MinValue = a.Min(g => g.Value),
        AvgValue = a.Average(g => g.Value)
    })
    .ToArray();

I would avoid using Descendants("param") unless you are validating the document in another way. Otherwise, if the document's format changes it might result in some unexpected results.

Here's what you want:

from p in samples.Elements("samples").Elements("measurement").Elements("param")
group (double)p by (string)p.Attribute("name") into grp
select new
{
    ParameterName = grp.Key,
    Min = grp.Min(),
    Avg = grp.Average(),
    Max = grp.Max()
}

Note the chained of Elements() calls, which makes precisely selecting down into the document very trivial, and casting of the XAttribute and XElement directly to double and string to get their values.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM