在LINQ中使用let时,如何处理丢失的XML节点

本文关键字:处理 节点 XML 何处理 LINQ let | 更新日期: 2023-09-27 18:29:30

我有一个很大的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条)时,我会收到未设置为对象实例的Object引用的错误。

这是有道理的,因为被选中的节点不在那里。在阅读了许多帖子后,我似乎应该做一个三元运算符来解释空父级。类似这样的东西:

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

这不太正确:无法确定条件表达式的类型,因为"string"answers"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()选角?

在LINQ中使用let时,如何处理丢失的XML节点

您可以使用以下表达式:

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

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

dotnetfiddle demo

演示显示,当wd:Home_Phone元素丢失时,homePhoneAttribute变量只包含空字符串。未引发异常。

更新:

如果您想要多个属性值(List<string>),而不仅仅是第一个,您可以按照如下方式修改上面的LINQ:

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