问个使用xpath 定位xml文档节点的问题

文盲老顾
WEB应用领新星创作者
博客专家认证
2013-03-20 12:25:31
昨天解决了 CSS3 的相邻和通用选择器用 xpath 实现的方法,今天遇到的问题是,子选择器如何实现

<html>
<head>
<title>js</title>
<style>
/*
> 1 子
~ 2 通用
+ 3 相邻
*/
.a>.b {font-style:italic;}
</style>
</head>
<body>
<div class="a">
<div class="b">1</div>
<div class="b"><span class="d">2</span></div>
<div class="b">3</div>
<div class="b">4</div>
<b isConvert="true">
<div class="b">5</div>
</b>
<i isConvert="true">
<b isConvert="true">
<div class="b">55</div>
</b>
</i>
<i>
<b isConvert="true">
<div class="b">555</div>
</b>
</i>
<span class="c">
<div class="b">6</div>
<div class="b">7</div>
<div class="b">8</div>
<div class="b">9</div>
<div class="b">10</div>
</span>
</div>
</body>


其中 .a>.b 就是子选择器的定义

原则上,应该只选择出 1,2,3,4 这四个 div 即可

XmlNodeList xnl1 = xml.SelectNodes("//*[contains(@class,'a')]/*[contains(@class,'b')]");


而实际工作中,我会向这个 xml 中追加一些信息,追加的内容中,统一包含一个属性 isConvert="true",我希望能够忽略这些标签,选择出的结果应该是 1,2,3,4,5,55 的 div 节点

即,我希望下列语句能合并成一个 xpath 实现

XmlNodeList xnl1 = xml.SelectNodes("//*[contains(@class,'a')]/*[contains(@class,'b')]");
XmlNodeList xnl2 = xml.SelectNodes("//*[contains(@class,'a')]/*[@isConvert]/*[contains(@class,'b')]");
XmlNodeList xnl3 = xml.SelectNodes("//*[contains(@class,'a')]/*[@isConvert]/*[@isConvert]/*[contains(@class,'b')]");


请教各位达人,这个 xpath 该如何书写?
...全文
141 6 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
6 条回复
切换为时间正序
请发表友善的回复…
发表回复
xiaolong199111 2013-04-05
  • 打赏
  • 举报
回复
文盲老顾 2013-03-20
  • 打赏
  • 举报
回复
-_-b,问题是,我无法确定在世纪工作中会追加多少层节点啊...
  • 打赏
  • 举报
回复
用 | 取并集 //*[contains(@class,'a')]/*[contains(@class,'b')] |//*[contains(@class,'a')]/*[@isConvert]/*[contains(@class,'b')] | //*[contains(@class,'a')]/*[@isConvert]/*[@isConvert]/*[contains(@class,'b')] 用Linq的Union
  • 打赏
  • 举报
回复
xml.SelectNodes("//*[@class='a']/*[@class='b']|//*[@class='a']/*[@isConvert='true']//*[@class='b' and parent::*[@isConvert='true']]")
测试通过,用Linq的All判断isConvert='true' 不过,这仅是针对你给出的文本通过,对于多级节点/*[@isConvert='true']//*[@class='b' 会有bug,对下面的节点也会匹配到,因为只判断了55父节点b,却忽略了span <i isConvert="true"> <span><b isConvert="true"> <div class="b">55</div> </b></span> 下面这样可以,最好写成递归的方式
var xml = XDocument.Parse(html);
                Func<XElement, bool> childFilter = e => e.Attribute("class") != null && e.Attribute("class").Value == "b";
                Func<XElement, bool> descendants = e => e.DescendantsAndSelf().Any(d => childFilter(d) 
                    && d.Ancestors().All(a => a.Attribute("isConvert") != null && a.Attribute("isConvert").Value == "true"));             
                var root = xml.Elements().First();
                root.SetAttributeValue("isConvert", "true");  //加这个是为了保证d.Ancestors().All反向匹配所有父节点
                foreach (var node in root.Elements().Where(e => childFilter(e) || descendants(e)))
                    Console.WriteLine(node.Value);
文盲老顾 2013-03-20
  • 打赏
  • 举报
回复
555 是不要的,凡是 .a 与 .b 之间没有其他节点,的,或者之间节点都带 isConvert 属性的才选中,否则不进行选中,我期望的结果就是6个
  • 打赏
  • 举报
回复
引用 2 楼 superwfei 的回复:
-_-b,问题是,我无法确定在世纪工作中会追加多少层节点啊...
那就用Linq to xml吧,传入参数Predicate<T> 你上面那个 <b isConvert="true"> <div class="b">555</div> </b>是排除掉还是你写掉了? .a > .b, i > b .b也是可以的,如果555是你写掉了,那就去掉 i >

111,098

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • AIGC Browser
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧