使用 LINQ 提取列表

Posted

技术标签:

【中文标题】使用 LINQ 提取列表【英文标题】:Using LINQ to extract list 【发布时间】:2021-08-12 06:15:36 【问题描述】:

我尝试使用这段代码提取列表。如果我这样做,顶点将始终只解析第一个点并跳过所有剩余的点。 (Vertices.Count == 1)

    Lines = xdoc.Descendants("LineEntity")
                 .Select(line => new LineEntity
                 
                     Id = (long)line.Element("Id"),
                     Name = (string)line.Element("Name"),
                     ConductorMaterial = (string)line.Element("ConductorMaterial"),
                     IsUnderground = (bool)line.Element("IsUnderground"),
                     R = (float)line.Element("R"),
                     FirstEnd = (long)line.Element("FirstEnd"),
                     SecondEnd = (long)line.Element("SecondEnd"),
                     LineType = (string)line.Element("LineType"),
                     ThermalConstantHeat = (long)line.Element("ThermalConstantHeat"),
                     Vertices = line.Descendants("Vertices")
                     .Select(p => new Point3D
                     
                         X = (double)p.Element("Point").Element("X"),
                         Y = (double)p.Element("Point").Element("Y"),
                         Z = 1
                     ).ToList()
                 ).ToList();

我要解析的 XML 文件如下所示

【问题讨论】:

你能把xml的图片替换成实际的xml作为文本吗? 出于好奇,为什么 x 和 y 点部分有 .Value 而其他部分没有? 我看到 R 中的 float 你不解析,但你在 X 和 Y 中为 double 解析,为什么?尝试像你为 float 做的投射而不解析和做 .Value,它会工作吗? @Mocas 尝试并编辑了代码,仍然只解析 Point 中的第一个值。 @Marco 几千行 【参考方案1】:

你需要从Vertices得到Point

Vertices = line.Descendants("Vertices")
               .Elements("Point")
               .Select(p => new Point3D
                 
                     X = (double)p.Element("X"),
                     Y = (double)p.Element("Y"),
                     Z = 1
                 )
Lines = xdoc.Descendants("LineEntity")
             .Select(line => new LineEntity
             
                 Id = (long)line.Element("Id"),
                 Name = (string)line.Element("Name"),
                 ConductorMaterial = (string)line.Element("ConductorMaterial"),
                 IsUnderground = (bool)line.Element("IsUnderground"),
                 R = (float)line.Element("R"),
                 FirstEnd = (long)line.Element("FirstEnd"),
                 SecondEnd = (long)line.Element("SecondEnd"),
                 LineType = (string)line.Element("LineType"),
                 ThermalConstantHeat = (long)line.Element("ThermalConstantHeat"),
                 Vertices = line.Descendants("Vertices").Elements("Point")
                 .Select(p => new Point3D
                 
                     X = (double)p.Element("X"),
                     Y = (double)p.Element("Y"),
                     Z = 1
                 ).ToList()
             ).ToList();

【讨论】:

【参考方案2】:

也许你需要这样的东西,或者沿着这条线 不完全确定它是怎么回事,但你明白了。

        Lines = xdoc.Descendants("LineEntity")
                 .Select(line => new LineEntity
                 
                     Id = (long)line.Element("Id"),
                     Name = (string)line.Element("Name"),
                     ConductorMaterial = (string)line.Element("ConductorMaterial"),
                     IsUnderground = (bool)line.Element("IsUnderground"),
                     R = (float)line.Element("R"),
                     FirstEnd = (long)line.Element("FirstEnd"),
                     SecondEnd = (long)line.Element("SecondEnd"),
                     LineType = (string)line.Element("LineType"),
                     ThermalConstantHeat = (long)line.Element("ThermalConstantHeat"),
                     Vertices = line.Descendants("Vertices")
                     .Select(v =>
                     
                         return v.Descendants("Ponit").Select(p =>
                         
                             new new Point3D
                             
                                 X = (double).Element("X"),
                                 Y = (double).Element("Y"),
                                 Z = 1
                             
                         
                      ).ToList()
                 ).ToList();

【讨论】:

【参考方案3】:

只需反序列化文档,无需手动解析:

[XmlRoot("root")]//here comes name of your top-level xml element, ie root
public class RootDoc

    public List<LineEntity> Lines  get; set; 

public class LineEntity

    public int Id  get; set; 
    public string Name  get; set;  
    [XmlArrayItem("Point")]
    public List<Point3D> Vertices  get; set; 


[TestFixture]
public class ParseTests

    [Test]
    public async Task Repro()
    
        var xdoc = XDocument.Parse(File.ReadAllText("in.xml"));
        var serializer = new XmlSerializer(typeof(RootDoc));

        var reader = xdoc.CreateReader();
        var parsed = (RootDoc)serializer.Deserialize(reader);
        reader.Close();
        
        parsed.Lines.ForEach(x=> x.Vertices.ForEach(y=> y.Z = 1)); //setting Z value like in your example.
        var lines = parsed.Lines;
    

示例 xml:

<root>
  <Lines>
    <LineEntity>
      <Id>123</Id>
      <Name>nn</Name>
      <Vertices>
        <Point>
          <X>123.123</X>
          <Y>234.234</Y>
        </Point>
      </Vertices>
    </LineEntity>
  </Lines>
</root>

附言

您的字段没有自定义映射逻辑,因此不要使标准程序过于复杂。

【讨论】:

以上是关于使用 LINQ 提取列表的主要内容,如果未能解决你的问题,请参考以下文章

从嵌套对象中提取值并使用 LINQ 对它们进行排序

如何使用 LINQ 从对象列表中获取唯一的属性列表?

如何从 Linq 查询中提取结果?

使用 LINQ 和实体框架在一个 SQL 查询中从多个表中提取数据

LINQ比较两个列表并将结果放入一个列表[重复]

从 XML 标记 C# LINQ 中提取键值