XElement扩展方法的问题

时间:2014-11-10 19:06:00

标签: c# xml linq extension-methods

我使用以下代码从XML文件中提取信息。但是如果节点不存在,我会收到NullReferenceException错误。我认为使用LINQ不应该发生这种情况。但就此而言,我对LINQ和XML非常陌生。所以我添加了一个我从here找到的扩展方法。但我仍然得到错误。有人可以告诉我我错过了什么吗?

using System;
using System.Text;
using System.Windows.Forms;
using System.Linq;
using System.Xml.Linq;

public static class XElementExtensionMethod
{
    public static string ElementValueNull(this XElement element)
    {
        if (element != null)
        {
            return element.Value;
        }
        else
        {
            return string.Empty;
        }
    }
}

namespace XMLReader
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            string file = @"c:\users\jim\desktop\XMLData - Copy.xml";
            XElement doc = XElement.Load(file);
            StringBuilder sb = new StringBuilder();

            var nodes = from node in doc.Elements("ClaimsSvcRs").Elements("ClaimDownloadRs")
                    select new
                    {
                        ClaimProbableAmount = (string)node.Element("ClaimsDownloadInfo").Element("ClaimsOccurrence").Element("ProbableIncurredAmt").Element("Amt").ElementValueNull()
                    };

        foreach (var node in nodes)
        {
            sb.Append("AMOUNT = ").Append(node.ClaimProbableAmount);
            MessageBox.Show(sb.ToString());
        }
    }
}
}

3 个答案:

答案 0 :(得分:2)

这是一个问题:

ClaimProbableAmount = (string)node.Element("ClaimsDownloadInfo").Element("ClaimsOccurrence").Element("ProbableIncurredAmt").Element("Amt").ElementValueNull()

由于您使用的是Element(),因此存在获得NRE的风险。 Element()返回指定元素的第一个实例或null。你在兔子洞里跳得太远了。

您可以通过多种方式解决此问题,更改除最后一个以外的所有来电以使用Elements()。这次它只会返回一个空集合,如果它们不存在的话。或者使用其他方法,例如等效的xpath查询。

答案 1 :(得分:1)

ElementValueNull中的问题几乎肯定是 ...之前的Element调用之一返回null。您正在调用实例方法(XContainer.Element())并且这是一个非常普通的实例方法 - 如果它在空引用上调用,它将像任何其他实例方法一样抛出异常将

避免这种情况的一个选择是反复使用Elements - 这样你最终会得到一个空序列,而不是空引用。最后使用FirstOrDefault获取单个XElement引用或null

此外:

  • 当您拥有单一财产时,使用匿名类型没有任何好处。只需选择字符串本身。
  • 当您在循环的每次迭代中调用StringBuilder时,使用ToString()没有任何好处。

我会将您的查询编写为:

var amounts = doc.Elements("ClaimsSvcRs").Elements("ClaimDownloadRs")
                 .Select(x => (string) x.Elements("ClaimsDownloadInfo")
                                        .Elements("ClaimsOccurrence")
                                        .Elements("ProbableIncurredAmt")
                                        .Elements("Amt")
                                        .FirstOrDefault() ?? "");

(请注意,您的扩展方法不需要 - 如果输入为string,则显式转换为null将返回null,然后空归合运算符将采用谨慎使用""代替null。)

答案 2 :(得分:0)

如果任何父元素为null(即ClaimsDownloadInfoClaimsOccurance等),那么您将获得NullReferenceExceptionElementValueNull扩展方法不会神奇地捕获它。您必须一次获取一个元素,并确认每个元素都不为空:

var claimsDownloadInfo = node.Element("ClaimsDownloadInfo");
if(claimsDownloadInfo != null)
{
    var claimsOccurance = claimsDownloadInfo.Element("ClaimsOccurrence");
    if(claimsOccurance != null)
    {
        //etc
    }
}