如何使用LINQ where子句忽略特定条件?

5

我有一个客户会发送一个XML数据源,我使用以下代码进行解析。这个代码是有效的。

reviews = from item in xmlDoc.Descendants("node")
                          select new ForewordReview()
                          {
                              PubDate = (string)item.Element("created"),
                              Isbn = (string)item.Element("isbn"),
                              Summary = (string)item.Element("review")
                          };

在获取所有“评论”后,我将IEnumerable强制转换为List并将其返回。最初,我很容易解析他们的XML,它看起来像这样:

<reviews>
    <node>
        <created>01-01-1900</created>
        <ISBN>12345657890123</ISBN>
        <Review>This is a nice and silly book</Review>
    </node>
    <node>
        <created>01-01-2011</created>
        <ISBN>1236245234554</ISBN>
        <Review>This is a stupid book</Review>
    </node>
    <node>
        <created>12-06-1942</created>
        <ISBN>1234543234577</ISBN>
        <Review>This is a old, naughty book</Review>
    </node>
</reviews>

然而,他们更改了它们的模式,我无法访问,现在他们的XML在结尾添加了一个最终的<node>标签,其中不包含我要查找的后代元素,因此我的解析在这个最后的标签上中断并抛出异常。下面是示例:

<reviews>
    <node>
        <created>01-01-1900</created>
        <ISBN>12345657890123</ISBN>
        <Review>This is a nice and silly book</Review>
    </node>
    <node>
        <created>01-01-2011</created>
        <ISBN>1236245234554</ISBN>
        <Review>This is a stupid book</Review>
    </node>
    <node>
        <created>12-06-1942</created>
        <ISBN>1234543234577</ISBN>
        <Review>This is a old, naughty book</Review>
    </node>
    <node>
        <count>4656</count>
    </node>
</reviews>

我需要知道是否有一种方法可以忽略这个最终标签(它总是出现在文档的末尾),即使它与我要查找的所有其他“节点”标签具有相同的名称。 我确实在此代码块周围使用了try-catch,但是如果它看到此错误,它不会返回good reviews列表。
谢谢大家。

1
也许可以添加一个 where item.Element("count") == null (或者反过来,"created" != null). - ordag
@ordag,我实际上也将你的建议和deltrees的一些空值检查结合到了我的解决方案中。谢谢。 - Isaiah Nelson
5个回答

4
如果它总是最后一个节点,
var nodes = xmlDoc.Descendants("node");
reviews = nodes.Take(nodes.Count() - 1).Select(...);

它始终是最后一个“节点”,而不是最后一个元素。 “reviews”元素是根元素。那有关系吗? - Isaiah Nelson
@fullNelson:这将获取除最后一个之外所有名为“node”的元素。你读过代码了吗? - Matti Virkkunen
我已经阅读了它。我从未使用过这种语法风格(因为我是LINQ的新手),所以我不太清楚它是如何工作的。 - Isaiah Nelson
@fullNelson:它们是完全正常的扩展方法调用。你肯定以前用过方法吧? - Matti Virkkunen

2
这样的代码应该能解决问题:
var reviews = from item in xmlDoc.Descendants("node").Where(x => x.Element("created") != null)
select new
{
    PubDate = (string)item.Element("created"),
    Isbn = (string)item.Element("isbn"),
    Summary = (string)item.Element("review")
};

如果您愿意,您可以为其他元素添加额外的空值检查。

1

添加空值检查

PubDate = (string)(item.Element("created") ?? ""),
Isbn = (string)(item.Element("isbn") ?? ""),
Summary = (string)(item.Element("review") ?? "")

无论你做什么,都要始终添加空值检查。这是一个好习惯。在这种情况下,它将消除此错误,但可能会在程序的后期出现一个错误,因为您假设这些字符串不为空,所以请确保稍后进行空值检查。


那么如果遇到奇怪的节点,它会将pubdate、isbn和summary加载到一个对象中,并给每个对象赋值为空字符串?如果是这样的话,我肯定可以在下游处理。 - Isaiah Nelson
确切地说,string.Empty可能是更好的选择,但这只是一个快速的草稿。 - deltree
@deltree 你的解决方案实际上是一个非常好的想法,并且揭示了我以前没有的空值检查。我也尝试了一个where子句,但你的方法更加可行。 - Isaiah Nelson

1
你可以计算节点的数量,然后使用这个重载的 Where 方法,它还会传递一个索引号:(http://msdn.microsoft.com/zh-cn/library/bb549418.aspx)
public static IEnumerable<TSource> Where<TSource>(
this IEnumerable<TSource> source,
Func<TSource, int, bool> predicate)

所以,类似这样的:

var count = xmlDoc.Descendants("node").Count();
xmlDoc.Descendants("node").Where((node,index) => index < count-1).Select(..)

我确定我知道这里发生了什么,但是代码语法的差异让我感到困惑。我认为你和Matti正在使用“点”符号表示法。对于我来说,在where语句中在子句之前添加此过滤器,我不知道该怎么做。 - Isaiah Nelson
@fullnelson 有些语句使用linq语法更容易编写/阅读,而有些语句使用扩展方法更容易编写/阅读(如上所述)。知道linq语句可以直接转换为扩展方法是很好的。 - MichaelvR
1
而且当你编写“LINQ语法”时,翻译正是编译器所做的,这使得整个语法有点多余。 - Matti Virkkunen
在编程方面,将以下内容从英语翻译成中文。仅返回翻译后的文本:我不完全同意你的看法。在我看来,使用“linq语法”可以使一些语句更易读,因此这并不是多余的。 - MichaelvR

0
你可以在那里加入一个“where”子句。
reviews = from item in xmlDoc.Descendants("node") where item.Descendants().Any(n => n.Name == "Review")
                      select new ForewordReview()
                      {
                          PubDate = (string)item.Element("created"),
                          Isbn = (string)item.Element("isbn"),
                          Summary = (string)item.Element("review")
                      };

这个示例只是检查名为“Review”的子节点,因此最好检查所有所需的子节点。


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