IEnumerable in LINQ 只有一个节点实例时

IEnumerable in LINQ when only one instance of the node

我正在尝试使用 Linq 查询一些 XML。我有以下 XML ...

的片段
<report>
  <invoice>
    <id>A4245</id>
    <date>12-20-2016</date>
    <amount>12.50</amount>
  </invoice>
<report>

这是我保存数据的模型

public class InvoiceModel
{
  public string InvoiceId { get; set; }
  public string InvoiceDate { get; set; }
  public string InvoiceAmount { get; set; }
}

这是 LINQ 查询

XDocument xml = XDocument.Parse(@"C:\Path\To\data.xml");
InvoiceModel invoice = xml.Descendants("invoice")
                       .Select(x => new InvoiceModel 
                       {
                         InvoiceId = x.Element("id").Value.ToString(),
                         InvoiceDate = x.Element("date").Value.ToString(),
                         InvoiceAmount = x.Element("amount").Value.ToString()
                       });

我得到的错误是..

Cannot implicitly convert type 'System.Collections.Generic.IEnumerable<InvoiceModel>' to 'InvoiceModel'. An explicit conversion exists (are you missing a cast?)

XML 中只会有一张发票。我如何告诉 LINQ 查询只有一个发票实例并且不需要 IEnumerable?

P.S。我将所有字符串都用于 属性 值,因为它们只是用作文本,不会被解析。

尝试

XDocument xml = XDocument.Parse(@"C:\Path\To\data.xml");
InvoiceModel invoice = xml.Descendants("invoice")
                       .Select(x => new InvoiceModel 
                       {
                         InvoiceId = x.Element("id").Value.ToString(),
                         InvoiceDate = x.Element("date").Value.ToString(),
                         InvoiceAmount = x.Element("amount").Value.ToString()
                       }).FirstOrDefault();

发生这种情况是因为 Descendants(...) returns 一个 IEnumerable<T>,然后用 Select 转换为 IEnumerable<InvoiceModel>。如果你知道最多有一个后代具有正确的名字,使用 FistsOrDefault() 检索它:

InvoiceModel invoice = xml.Descendants("invoice")
    .Select(x => new InvoiceModel {
        InvoiceId = x.Element("id").Value.ToString(),
        InvoiceDate = x.Element("date").Value.ToString(),
        InvoiceAmount = x.Element("amount").Value.ToString()
    }).FirstOrDefault();
// Make sure that you get an invoice
if (invoice == null) {
    throw new InvalidOperationException("Invoice element does not exist in XML document.");
}

您可以使用 .FirstOrDefault() 到 select 唯一的 invoice 元素:

InvoiceModel invoice = xml.Descendants("invoice")
   .Select(x => new InvoiceModel
   {
       InvoiceId = x.Element("id").Value.ToString(),
       InvoiceDate = x.Element("date").Value.ToString(),
       InvoiceAmount = x.Element("amount").Value.ToString()
   })
   .FirstOrDefault();

您的代码中还有其他问题:

  • 您想使用 XDocument.Load 加载文件,Parse 用于原始 xml。
  • xml 未正确终止。最后一个标签应该是 </report>.

.Select()returns一个合集。如果该集合中只有一个元素,则追加:

.Single()

如果可以有多个而你想要第一个:

.First()

如果应该有但可能是 none:

.SingleOrDefault()

等等

您可以应用多种逻辑,任何 IEnumerable<T> 都有多种扩展方法。基本上,如果您只想要一个,select 就是 .Select().

返回的集合中的那个