Linq to Xml - 空元素

4
我正在使用Linq解析一个XML文档,但我遇到了一种情况,不确定如何处理。 我尝试过的所有方法似乎都无效。 如下所示(非常简单的场景)中OrderAmount为空。 这是一个十进制字段。 当我在LINQ中尝试处理它时,它一直出错。 我尝试过使用decimal?,使用null检查等,但似乎都没有起作用(很可能是用户错误)。 如何处理元素为非字符串(例如十进制数)且为空的情况?
XML:
<Orders>
  <Order>
    <OrderNumber>12345</OrderNumber>
    <OrderAmount/>
  </Order>
</Orders>

LINQ:
List<Order> myOrders = from orders in xdoc.Descendants("Order")
                       select new Order{
                       OrderNumber = (int)orders.Element("OrderNumber"),
                       OrderAmount = (decimal?)orders.Element("OrderAmount"),


}.ToList<Order>();
4个回答

5
XElement类型向decimal?类型的转换仅在元素实际上为null时返回null
我认为最易读的解决方案应该是这样的:
elem.IsEmpty ? null : (decimal?)elem

如果您经常使用此功能,可以将其放入扩展方法中。或者在LINQ查询中使用let,以避免重复选择元素的代码。

from orders in xdoc.Descendants("Order")
let amountElem = orders.Element("OrderAmount")
select new Order
{
    OrderNumber = (int)orders.Element("OrderNumber"),
    OrderAmount = amountElem.IsEmpty ? null : (decimal?)amountElem
}

另一个选择是,如果您可以更改XML,就可以简单地省略表示null的元素。它应该可以很好地与您已有的代码配合使用。
编辑:扩展方法可能如下所示:
static class XElementExtensions
{
    public static decimal? ToNullableDecimal(this XElement elem)
    {
        return elem.IsEmpty ? null : (decimal?)elem;
    }
}

你可以像这样使用它:

OrderAmount = orders.Element("OrderAmount").ToNullableDecimal()

嗨@svick...你能给我展示一个在这里应用扩展方法的例子吗?感谢信息! - scarpacci

3
如何呢?
OrderAmount = String.IsNullOrEmpty(orders.Element("OrderAmount").Value) ? 
                  default(decimal?) : Decimal.Parse(orders.Element("OrderAmount").Value),

根据svick的评论,这样可能更安全。
OrderAmount = String.IsNullOrEmpty(orders.Element("OrderAmount").Value) ? 
                  default(decimal?) : (decimal)orders.Element("OrderAmount"),

1
使用 decimal.Parse() 是危险的,因为它使用特定于文化的格式来进行解析。它可能在您的计算机上运行良好,但在我的计算机上则不一定如此。我认为在这方面使用从 XElement 转换是安全的。 - svick
@svick - 没错 - 很容易忘记其他文化 - 我不是一个足够优秀的开发者,能够编写被全世界使用的东西 :) - Adam Rackis

0

我认为你想要的是元素的值而不是元素本身。

编辑:如果你真的坚持要保留转换,这里有一个通过的单元测试,你可以使用它来得到一个更令人愉悦的格式:

    private XDocument BuildDocument()
    {
        var myAmount = new XElement("OrderNumber", 12345);
        var myNumber = new XElement("OrderAmount");
        var myOrder = new XElement("Order", myAmount, myNumber);
        var myOrders = new XElement("Orders", myOrder);
        return new XDocument(myOrders);
    }

    private class Order
    {
        public int OrderNumber { get; set; }
        public decimal? OrderAmount { get; set; }
    }

    [TestMethod]
    public void TestMethod()
    {
        var myDoc = BuildDocument();
        List<Order> myOrders = (from orders in myDoc.Descendants("Order")
                               select new Order
                               {
                                   OrderNumber = (int)orders.Element("OrderNumber"),
                                   OrderAmount = GetAmount(orders.Element("OrderAmount"))
                               }).ToList<Order>();
    }

    private static decimal? GetAmount(XElement e)
    {
        if (e == null || string.IsNullOrEmpty(e.Value))
        {
            return 0.0M;
        }
        return (decimal?)e;
    }

(您仍需添加代码以处理OrderAmount的值为“Asdf”之类的情况)


这就是强制类型转换的作用。它们访问元素的值并将其转换为指定的类型(如果可能)。但它使用与所讨论的 XML 不同的空值规则。 - svick
好的,这里的代码调用了 System.Number.ParseDecimal(string) 方法,但它似乎无法处理 null 值。你需要先将值转换为字符串,然后再进行类型转换为 decimal,并提供一个默认值以应对解析失败的情况(例如包含非数字字符、为空或为 null 等)。 - Erik Dietrich

0
你在 Order 类中如何声明 OrderAmount?试着声明一下。
public decimal? OrderAmount { get; set; }

这不是问题所在。如果是的话,它会导致编译时错误,而不是数据相关的运行时错误。 - svick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接