【问题标题】:Using XPathEvaluate to get text node value in Linq-to-Xml query使用 XPathEvaluate 获取 Linq-to-Xml 查询中的文本节点值
【发布时间】:2012-04-05 17:33:00
【问题描述】:

我最近遇到了一种情况,我必须使用 XPath 作为 Linq to XML 查询的一部分,发现它的行为有点奇怪。我确信还有其他方法可以使用本机 XElement API 方法解决问题,但在这种情况下,XPath 更适合定位我需要的特定 XML 文本值,因为文档结构非常复杂。我最终找到了一种让它工作的方法(见下文),但它不是很漂亮,这通常意味着我错过了一些东西。

我的问题是如何使用XPathEvalute 或其他一些XPath 方法来返回可在where 语句中使用或作为select 方法中新对象的一部分的字符串值。我找到了一个使用大量铸造和Null coalesing operator in LINQ 的解决方案,但必须有一种更清洁的方法。问题的根源在于 XPathEvaluate 方法通常会返回一个 XText 元素(强制转换为通用对象),但 Linq 将其转换为 IEnumerable 以便它可以延迟执行。当您尝试访问该对象的值时,这会导致问题,因为在执行查询之前它不会具体化。

下面是示例:使用以下 XML 文档查找所有拥有同名男孩和女孩的父母。这必须使用 XPath 查询来完成,因为在我的情况下,文档结构非常复杂。

//Use C# Program Language type in LinqPad
void Main()
{
    var xml = XElement.Parse(@"<Root>
        <Parent>
            <Boy>Anne</Boy>
        </Parent>
        <Parent>
            <Boy>Alex</Boy>
            <Girl>Alex</Girl>
        </Parent>
        <Parent>
            <Boy>Jay</Boy>
        </Parent>
    </Root>");

    var q1 = from e in xml.XPathSelectElements("/Parent")
             select e;

    q1.Dump();

    var q2 = from e in q1
             let boy = ((IEnumerable<Object>)e.XPathEvaluate("Boy/text()")).Cast<System.Xml.Linq.XText>().FirstOrDefault() ?? new System.Xml.Linq.XText("")
             let girl = ((IEnumerable<Object>)e.XPathEvaluate("Girl/text()")).Cast<System.Xml.Linq.XText>().FirstOrDefault() ?? new System.Xml.Linq.XText("")
             select new {t=boy.GetType(), b=boy, g=girl, same=(boy.Value==girl.Value)};

    q2.Dump();
}

结果是:

您可以看到第二个元素被标识为具有同名的男孩和女孩。在仍然使用 XPath 的同时是否有任何更简洁的方法可以做到这一点,或者我是否坚持使用上面的代码。

【问题讨论】:

  • 您可以简化您的 XPath 以获取所有符合您正在寻找的标准的父母://Parent[Boy/text()=Girl/text()]

标签: c# xpath linq-to-xml


【解决方案1】:

是否可以使用XPathSelectElement 代替XPathEvaluate?

var q2 = from e in q1
         let boy = (string)e.XPathSelectElement("Boy")
         let girl = (string)e.XPathSelectElement("Girl")
         where boy.StartsWith("A") //Optional use in where statement
         select new { t = boy.GetType(), b = boy, g = girl, same = (boy == girl) };

【讨论】:

  • 这比我的要好 :( 这完全取决于转换为(字符串)。
  • XElement 转换运算符非常方便。
  • 非常好!我试过了,但我需要文本节点而不是完整元素。看起来我所需要的只是转换成一个字符串。谢谢!
  • 我真正的目标是在 where 语句中使用它,所以我更新了你的答案,将转换作为 let 语句的一部分。现在完美运行!
【解决方案2】:

这个怎么样,不知道你是否需要't'。

var q2 = q1.Select (q => 
    new 
    { 
        Boy  = q.XPathSelectElement("Boy"),
        Girl = q.XPathSelectElement("Girl"),
    }
);

var q3 = q2.Select (q => 
    new 
    {
        Boy = q.Boy == null ? string.Empty : q.Boy.Value, 
        Girl = q.Girl == null ? string.Empty : q.Girl.Value
    } 
);

var q4 = q3.Select (q => new { q.Boy, q.Girl, same=q.Boy==q.Girl });

q4.Dump();

可以改写为

var q2 = q1.
    Select (q => new { Boy  = q.XPathSelectElement("Boy"),Girl = q.XPathSelectElement("Girl"),}).
    Select (q => new { Boy = q.Boy == null ? string.Empty : q.Boy.Value, Girl = q.Girl == null ? string.Empty : q.Girl.Value}).
    Select (q => new { q.Boy, q.Girl, same=q.Boy==q.Girl }
);

q2.Dump();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多