【问题标题】:C# ?? null coalescing operator LINQC# ??空合并运算符 LINQ
【发布时间】:2012-09-23 06:52:58
【问题描述】:

当我使用 LINQ 将 XML 文件解析为自定义对象时,我试图防止出现 NULL 值。

我在Scott Gu's blog 上找到了一个很好的解决方案,但由于某种原因,它不适用于我的整数。我想我使用了相同的语法,但似乎我遗漏了一些东西。哦,由于某种原因,它在节点不为空时起作用。

以下是我的代码摘录。

List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname")
        select new GrantAgresso()
        {
             Year = (int?)g.Element("yearnode") ?? 0,
             Subdomain = (string)g.Element("domainnode") ?? ""
        }).ToList();

错误信息是:

输入字符串的格式不正确。

如果有人知道我做错了什么,请帮忙:)

编辑:一段 XML(奇怪的名字,但不是自愿选择)

<Agresso>
  <AgressoQE>
    <r3dim_value>2012</r3dim_value>
    <r0r0r0dim_value>L5</r0r0r0dim_value>
    <r7_x0023_province_x0023_69_x0023_V005>0</r7_x0023_province_x0023_69_x0023_V005>
    <r7_x0023_postal_code_x0023_68_x0023_V004 />
    <r7_x0023_country_x0023_67_x0023_V003>1004</r7_x0023_country_x0023_67_x0023_V003>
    <r7_x0023_communitydistrict_x0023_70_x0023_V006>0</r7_x0023_communitydistrict_x0023_70_x0023_V006>
  </AgressoQE>
</Agresso>

【问题讨论】:

  • 我赌的是不正确的 xml 语法。你能显示xml内容吗?合并运算符是正确的,如果不是,它不会抛出此错误(它与字符串无关)
  • YearSubdomain 赋值会引发异常吗?您可以尝试评论其中一行以查看异常是否消失。
  • 我在帖子中添加了 xml 的一个节点。触发异常的是空节点(以 r7_ 开头)。我用来检索它的代码与我一年使用的代码相同。

标签: c# xml linq null-coalescing-operator


【解决方案1】:

抛出的是这个表达式:

(int?)g.Element("yearnode")

这是因为如果元素的文本节点的实际值为String.Empty 而不是null,由于空字符串不是Int32.Parse 的有效格式,尝试的转换失败。

如果您的 XML 中完全缺少该元素,这将按预期工作,但如果有一个空标签 &lt;yearnode/&gt;&lt;yearnode&gt;&lt;/yearnode&gt;,您将收到异常。

【讨论】:

  • 这可能是我一直在寻找的答案。你会建议我做什么?
【解决方案2】:

如果元素不存在、元素为空或包含无法解析为整数的字符串,以下扩展方法将返回 0:

    public static int ToInt(this XElement x, string name)
    {
        int value;
        XElement e = x.Element(name);
        if (e == null)
            return 0;
        else if (int.TryParse(e.Value, out value))
            return value;
        else return 0;
    }

你可以这样使用它:

...
Year = g.ToInt("r3dim_value"),
...

或者如果你准备好考虑反射成本并接受任何值类型的默认值,你可以使用这个扩展方法:

public static T Cast<T>(this XElement x, string name) where T : struct
{
    XElement e = x.Element(name);
    if (e == null)
        return default(T);
    else
    {
        Type t = typeof(T);
        MethodInfo mi = t.GetMethod("TryParse",
                                    BindingFlags.Public | BindingFlags.Static,
                                    Type.DefaultBinder,
                                    new Type[] { typeof(string), 
                                                 t.MakeByRefType() },
                                    null);
        var paramList = new object[] { e.Value, null };
        mi.Invoke(null, paramList);
        return (T)paramList[1]; //returns default(T), if couldn't parse
    }
}

并使用它:

...
Year = g.Cast<int>("r3dim_value"),
...

【讨论】:

  • 我使用了你的第二个建议。谢谢!
【解决方案3】:

你可以加Where operator

.....
.Where(a => ! string.IsNullOrEmpty(a)).ToList();

【讨论】:

  • a 应该在这里是什么?你为什么要在看起来像 IEnumerable&lt;string&gt; 的地方打电话给 ToList
  • 这不会做 OP 想要做的事情。
  • 不,我不能,我不想过滤结果,我只是不希望我的程序在出现空节点时中断......
【解决方案4】:

如果 year 为 null 或 empty 字符串,您将收到“输入字符串格式不正确”异常。您可以编写一个扩展方法来读取值。下面的代码我没有测试过,但它可能会给你一些提示。

public static ReadAs<T>(this XElement el, T defaultValue) {
  var v = (string)el; // cast string to see if it is empty

  if (string.IsNullOrEmpty(v)) // test
    return defaultValue;

  return (T)el; // recast to our type.
}

【讨论】:

    【解决方案5】:

    消息Input string was not in a correct format 看起来像int.parse () 抛出的消息,因此可能是您有一个带有值(非空)但无法成功解析为整数值的yearnode

    这样的事情可能会解决它:

    List<GrantAgresso> lsResult = (from g in xml.Element("root").Elements("Elementname")
        let yearNode = g.Element("yearnode")
        select new GrantAgresso
        {
             Year = string.IsNullOrWhiteSpace(yearNode.Value) ? 0 : int.Parse(yearNode.Value),
             Subdomain = g.Element("domainnode").Value
        }).ToList();
    

    有几点需要注意:

    select new GrantAgresso - 对于带有对象初始值设定项的默认构造函数,您不需要括号。

    string.IsNullOrWhiteSpace - 在 .net 4.0 中引入,如果您使用的是 3.5 或更早版本,请使用 string.IsNullOrEmpty

    g.Element("domainnode").Value - 总是返回一个字符串

    如果您希望 Year 使用 null 而不是 0,请使用 (int?)null 而不是 0

    【讨论】:

    • 嗨,我刚刚对我的 XML 进行了两次和三次检查,我想转换为 int 的值要么是空的,要么是有效的整数。这也是我考虑过的;-)
    • 我已经更新了我的答案,提出了处理空瓶的建议。
    【解决方案6】:

    您的问题是从 XElement 到 int 的转换?对 XElement 的字符串值使用 int.Parse 方法,在您的情况下是 String.Empty。以下导致相同的错误:

    XElement x = new XElement("yearnode", String.Empty);
    int? a = (int?)x; // throws FormatException
    

    您可以通过首先将 XElement 转换为字符串并检查它是否为 null 或空并且仅在不转换为 int? 的情况下避免这种情况。为此,请替换

    Year = (int?)g.Element("yearnode") ?? 0,
    

    Year = !string.IsNullOrEmpty((string)g.Element("yearnode")) ? (int?)g.Element("yearnode") : 0,
    

    它不漂亮,如果字符串不合法,它仍然会抛出,但如果你可以假设元素始终是整数或 null/空,它就会起作用。

    【讨论】:

      猜你喜欢
      • 2011-02-13
      • 2011-03-26
      • 2012-09-19
      • 2018-12-12
      • 1970-01-01
      • 2011-11-08
      • 1970-01-01
      • 2013-09-13
      • 2015-03-27
      相关资源
      最近更新 更多