【问题标题】:How do I handle missing XML nodes when working with let in LINQ?在 LINQ 中使用 let 时如何处理丢失的 XML 节点?
【发布时间】:2016-01-08 21:29:41
【问题描述】:

我有一个非常大的 XML 文档,其中缺少一些节点。

好的 XML:

<wd:Job_Family wd:Descriptor="Research/Extension">
<wd:Home_Phone wd:Descriptor="+1 (555) 555-0731">
  <wd:ID wd:type="WID">89bfac800b6b41da94e1d1a22b14e66a</wd:ID>
</wd:Home_Phone>
<wd:Home_Address wd:Descriptor="1 Beverly Dr">

错误的 XML:

 <wd:Job_Family wd:Descriptor="Research/Extension">
     ***MISSING ***
<wd:Home_Address wd:Descriptor="1 Beverly Dr">

我选择数据的方式是这样的:

List<Employee> employee = new List<Employee>();
try
{
    // Get the xml file as stream  
    StreamReader reader = new StreamReader(outputFileNameAndPath);

    // Read the whole contents and return as a string  
    string xmlString = reader.ReadToEnd();

    XDocument doc = XDocument.Parse(xmlString);
    XNamespace wd = "urn:com.something.report/Worker_ID_Data_-_Store";

    IEnumerable<XElement> worker = doc.Descendants(wd + "Report_Entry");

    var query = (from x in doc.Descendants(wd + "Report_Entry")

                 let jobFamilyAttribute = x.Element(wd + "Job_Family_Group").Attributes(wd + "Descriptor").FirstOrDefault()

                 select new Employee
                 {
                    JobFamily = jobFamilyAttribute.Value
                 });
    employee.AddRange(query);
}
catch (Exception ex)
{
    log.Error("An error occurred while trying to parse the XML: " + ex);
}
return employee;

效果很好。我选择了一些额外的节点,但为了简单起见,这就足够了。

现在,当我尝试选择一个缺失的节点(可能是 1000 条记录中的 1 条)时,我收到未将对象引用设置为对象实例的错误。

这是有道理的,因为被选中的节点不存在。 在阅读了很多帖子之后,看起来我应该做一个三元运算符来解释 null 父级。像这样的:

let homePhoneAttribute = x.Element(wd + "Home_Phone") == null ? "" : x.Element(wd + "Home_Phone").Attributes(wd + "Descriptor").FirstOrDefault()

这不太对: 无法确定条件表达式的类型,因为 'string' 和 'System.Xml.Linq.XAttribute' 之间没有隐式转换

即使我将它转换为字符串或对象,它也会编译,但是当我添加范围时,我只会得到所有记录的第一个值:

let homePhoneAttribute = x.Element(wd + "Home_Phone") == null ? (string)"" : x.Element(wd + "Home_Phone").Attributes(wd + "Descriptor").FirstOrDefault().Value

那么,我的冗长问题是,我如何正确地转换和考虑空节点,同时仍然能够使用 let 进行选择?

我认为它必须是某种DefaultIfEmpty() 铸造?

【问题讨论】:

  • 您获得了第一条记录,因为您在查询中有FirstOrDefault()。稍微不同的查询会起作用吗 - let homePhoneAttribute = (string)x.Element(wd + "Home_Phone") == null ? String.Empty : x.Element(wd + "Home_Phone").Attributes(wd + "Descriptor"); - 请注意,这会给你一个集合,然后你可以用你想要的东西来做。
  • 感谢蒂姆的帮助......这也不太奏效。仍然存在转换问题:无法确定条件表达式的类型,因为 'string' 和 'System.Collections.Generic.IEnumerable' 之间没有隐式转换
  • 可能是这样的:let homePhoneAttribute = (string)x.Element(wd + "Home_Phone") == null ? null : x.Element(wd + "Home_Phone").Attributes(wd + "Descriptor"); - 将String.Empty 替换为null。尽管这可能会导致下游问题。也许不是null,而是使用new IEnumerable&lt;XAttribute&gt;();或类似的东西?
  • 嗨,蒂姆 - 我仍然需要选择 XAttribute Value. homePhoneAttribute.FirstOrDefault().Value. 当我这样做时,它会再次中断转换。如果我不使用 FirstOrDefault(),我会得到这个 'IEnumerable&lt;XAttribute&gt;' does not contain a definition for 'Value' and no extension method 'Value' accepting a first argument of type 'IEnumerable&lt;XAttribute&gt;' could be found

标签: c# xml linq


【解决方案1】:

您可以使用以下表达式:

let homePhoneAttribute = (string)x.Elements(wd + "Home_Phone")
                                  .Attributes(wd + "Descriptor")
                                  .FirstOrDefault()

通过使用x.Elements() 而不是单数形式Element(),您可以避免在找不到元素的情况下出现异常。然后,通过将FirstOrDefault() 返回值转换为string,您可以避免返回值为null 的异常。

dotnetfiddle demo

演示显示homePhoneAttribute 变量在缺少wd:Home_Phone 元素时仅包含空字符串。没有抛出异常。

更新:

如果你想要多个属性值 (List&lt;string&gt;) 而不仅仅是第一个,你可以修改上面的 LINQ 如下:

let homePhoneAttribute = x.Elements(wd + "Home_Phone")
                          .Attributes(wd + "Descriptor")
                          .Select(o => o.Value)
                          .ToList()

【讨论】:

  • 你是对的,它可以正确投射,但它也只选择第一个电话号码:select new Employee { JobFamily = jobFamilyAttribute.Value, HomePhone = homePhoneAttribute, }); employee.AddRange(query);
  • @MattKnight 检查上面的 update 部分。我假设你想要List&lt;string&gt; 作为`HomePhone` 属性的值
  • 感谢您的帮助。当我使用 .ToList() 时,我仍然需要在选择中指定一些内容:select new Employee { JobFamily = jobFamilyAttribute.Value, HomePhone = homePhoneAttribute need something here, }); 我需要选择 XAttribute.Value,就像我为 JobFamily 所做的那样。如果我使用 FirstorDefault(),每条记录都分配有相同的电话号码。也许我应该添加更多数据并在 .Net Fiddle 上与您合作?
  • @MattKnight 当然,提供包含基本代码和代表性示例数据的 .NET fiddle 可以方便人们提供帮助
  • 你的回答成功了,let homePhoneAttribute = x.Elements(wd + "Home_Phone") .Attributes(wd + "Descriptor") .Select(o =&gt; o.Value) .ToList() 然后在选择中我使用了HomePhone = homePhoneAttribute.FirstOrDefault(), 谢谢你的帮助!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-06
相关资源
最近更新 更多