【问题标题】:Check if XElement is null globally检查 XElement 是否全局为空
【发布时间】:2011-10-26 07:10:23
【问题描述】:

我有一个班级负责读取和保存 XML 文件。 现在它的一个简单版本如下所示:

public class EstEIDPersoConfig
{
    public bool LaunchDebugger { get ; set; }
    public string Password { get; set; }
    public int Slot { get; set; }
    public string Reader { get; set; }
    public string TestInput { get; set; }
    public bool Logging { get; set; }

    public EstEIDPersoConfig()
    {
        XElement xml = XElement.Load(myxml.xml);
        XElement Configuration = xml.Element("Configuration");

        LaunchDebugger = Convert.ToBoolean(Configuration.Element("LaunchDebugger").Value);
        Password = Configuration.Element("Password").Value;
        Slot = Convert.ToInt32(Configuration.Element("Slot").Value);
        Reader = Configuration.Element("Reader").Value;
        TestInput = Configuration.Element("TestInput").Value;
        Logging = Convert.ToBoolean(Configuration.Element("Logging").Value);
     }
 }

以后还会有更多。所以问题是如果xml中不存在某些元素,我会得到System.NullReferenceException。所以我需要检查元素是否为null。这是一种方法:

var value = Configuration.Element("LaunchDebugger").Value;
if (value != null)
    LaunchDebugger = Convert.ToBoolean(value);
else
    throw new Exception("LaunchDebugger element missing from xml!");

但是对每个元素都这样做就太过分了。所以我需要一些好的想法来简化这个系统,这样它就不会变成 1000 行代码。

编辑:编辑最后一个代码sn-p,想法不是设置默认值,想法是通知用户xml中缺少此元素whats null。

【问题讨论】:

  • 当 xml 的值为 null 时,对相应的构造函数执行所谓的 xml 惰性初始化。
  • @hs2d - 我认为 abatishchev 的答案正是您想要的。

标签: c# .net xml linq-to-xml


【解决方案1】:

这里的想法直接来自 abatischev 的回答,因此他值得称赞。

按照 Microsoft here 的规定,您可以将 XElement 强制转换为您想要的类型。

LaunchDebugger = (bool?)Configuration.Element("LaunchDebugger");

如果你想处理null 的情况,我想你可以做到

LaunchDebugger = (bool)(Configuration.Element("LaunchDebugger") ?? true);

或许

LaunchDebugger = (bool)(Configuration.Element("LaunchDebugger") ?? false);

取决于您的业务逻辑。如果您为特定类型执行相同的合并场景,则可能适合将这一衬里包装在方法、扩展或其他方式中,但我不确定它会增加多少。

【讨论】:

  • 看来需要使用bool?
  • +1 使用 ?? (我相信空合并运算符?)在空元素的情况下设置默认值。今晚我从这个帖子中学到了两件有用的东西:)
  • 除了,这不适用于 OP 示例代码:元素可能存在,但没有值...
  • 如果元素存在但没有值,上面怎么会不起作用?这不等于元素根本不存在吗?
【解决方案2】:
(bool)Configuration.Element("LaunchDebugger")

(bool?)Configuration.Element("LaunchDebugger")

不应抛出异常。

参见 MSDN:

【讨论】:

  • 为什么要投反对票,这对我来说似乎是最好的答案msdn.microsoft.com/en-us/library/bb387049.aspx
  • @Jodrell:你似乎是对的……我的错。不过看起来有点奇怪!
  • 不应该是(bool?)Configuration.Element("LaunchDebugger")吗?即,使 bool 成为可为空的类型。
  • +1 因为这看起来是最优雅的解决方案。只需用 bool 替换 bool 和 int 属性?和int?并使用演员表。
  • @Jodrell:感谢您的链接。反对者应该经常阅读 MSDN
【解决方案3】:

我有一个扩展方法,只用于这种事情:

    public static T GetValue<T>(
            this XElement @this,
            XName name, 
            Func<XElement, T> cast, 
            Func<T> @default)
    {
        var e = @this.Element(name);
        return (e != null) ? cast(e) : @default();
    }

它为您提供所需的强制转换以及默认值工厂。

以下是您的使用方法:

LaunchDebugger = Configuration.GetValue("LaunchDebugger",
    x => Convert.ToBoolean(x), () => false);
Password = Configuration.GetValue("CMKPassword", x => (string)x, () => "");
Slot = Configuration.GetValue("CMKSlot", x => (int)x, () => -1);
Reader = Configuration.GetValue("Reader", x => (string)x, () => "");
TestInput = Configuration.GetValue("TestInput", x => (string)x, () => "");
Logging = Configuration.GetValue("Logging",
    x => Convert.ToBoolean(x), () => false);

【讨论】:

  • 我看不出将两个 lambdas 传递给泛型比仅转换和使用 ?? 空合并运算符更好
  • 为什么是第二个 lambda 而不是 "T" 类型的普通值?
  • @Jodrell - cast lambda 确实更好地被认为是一种转换。我这样做是为了使代码保持一致。
  • @HansKesting - 第二个 lambda 是延迟计算的,因此如果该值的计算成本很高,那么它仅在需要时执行。
【解决方案4】:

将逻辑提取到方法中,并为Int32、boolean和其他数据类型转换提供重载方法。

public static void GetElementValue(XElement xElement, string parameter, out bool value)
    {
        var stringValue = xElement.Element(parameter).Value;
        value = false;
        if (value != null)
            value = Convert.ToBoolean(stringValue);
    }

【讨论】:

  • 我不会使用out 参数,而是使用返回值。如果字符串“LaunchDebugger”是一个参数会更有用。
  • 但是这样我仍然需要为每个 xml 元素创建外部方法?或者我在这里错过了什么..
  • 我看不出为每个属性编写一个方法会有帮助
  • @hs2d 查看更新后的代码。它应该适合你。
【解决方案5】:

您可以定义一种方法来为您提取值并在那里对 null 进行一些检查。因此,将值检索包装在您自己的方法中,如下所示:

public string GetXMLValue(XElement config, string elementName){
    var element = Configuration.Element(elementName);

    if(element == null)
        return String.Empty;

    return element.Value;
}

当然,您可以扩展它以正确解析为布尔值等。

【讨论】:

  • 为什么框架开发者不添加这个?回答,因为它不需要。请参阅 abatischev 的回答。
  • @Jodrell:我现在明白了....我想我实际上从这篇文章中学到了一些东西;)谢谢伙计!
【解决方案6】:

外部方法怎么样:

public static class XElementExtensions
{
    public static bool AsBoolean(this XElement self, bool defaultValue)
    {
        if (self == null)
        {
            return defaultValue;
        }
        if (!string.IsNullOrEmpty(self.Value))
        {           
            try
            {
                return XmlConvert.ToBoolean(self.Value);
            }
            catch
            {
                return defaultValue;
            }
        }
        return defaultValue;
    }
}

我已经用 SnippetCompiler 对此进行了测试:

XElement test = new XElement("test", 
    new XElement("child1"),
    new XElement("child2", new XText("true")),
    new XElement("child3", new XText("false")),
    new XElement("child4", new XText("rubbish")));

WL(test.Element("child1").AsBoolean(false)); // note, "child1" has no value (or is `""`)
WL(test.Element("child2").AsBoolean(false));
WL(test.Element("child3").AsBoolean(false));
WL(test.Element("child4").AsBoolean(false));
WL(test.Element("child5").AsBoolean(false)); // note, "child5" doesn't exist        

要产生这个结果:

False
True
False
False
False

为其他类型添加更多此类方法并添加AsBoolean(defaultValue),因为当您想默认为true时,它可以派上用场!

正如其他人所说,您可以使用?? 运算符为null 提供值。不过,这不会嵌套,所以:

LaunchDebugger = XmlConvert.ToBoolean(Configuration.Element("LaunchDebugger").Value) ?? false;

如果 XML 文件中没有这样的元素,将通过 NullReferenceException

【讨论】:

  • 使用?? 合并运算符在abatischev 的回答中使用微软规定的方法不是很简单吗?
猜你喜欢
  • 2019-12-16
  • 1970-01-01
  • 2022-12-08
  • 1970-01-01
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多