繁体   English   中英

如何从当前上下文节点查找最接近的匹配项

[英]How to find nearest match from current context node

我有一个相当大的XML文件,我正在尝试使用C#应用程序和HtmlAgilityPack进行解析。 XML看起来像这样:

...
<tr>
<td><b>ABC-123</b></td>
<td>15</td>
<td>4</td>
</tr>
<tr>
<td>AB-4-320</td>
<td>11</td>
<td>2</td>
</tr>
<tr>
<td><b>ABC-123</b></td>
<td>15</td>
<td>4</td>
</tr>
<tr>
<td>AB-4-320</td>
<td>11</td>
<td>2</td>
</tr>
<tr>
<td>CONTROLLER1</td>
<td>4</td>
<td>3</td>
</tr>
<td>CONTROLLER2</td>
<td>4</td>
<td>3</td>
</tr>
...

基本上是一系列重复的表行和列。 我首先使用以下方法搜索控制器:

string xPath = @"//tr/td[starts-with(.,'CONTROLLER2')]";
HtmlNodeCollection nodes = doc.DocumentNode.SelectNodes(xPath);
foreach (HtmlNode link in nodes) { ... }

返回正确的节点。 现在我想向后(向上)搜索以文本“ABC”开头的第一个(最近的)匹配<td>节点:

string xPath = @link.XPath + @"/parent::tr/preceding-sibling::tr/td[starts-with(.,'ABC-')]";

这将返回所有匹配的节点,而不仅仅是最近的节点。 当我试图将[1]添加到此XPath字符串的末尾时,它似乎不起作用,我发现没有示例显示谓词与这样的轴函数一起使用。 或者,更有可能的是,我做错了。 有什么建议么?

您可以使用此XPath:

/parent::tr/preceding-sibling::tr[td[starts-with(.,'ABC-')]][1]/td[starts-with(.,'ABC-')]

这将搜索具有子<td>以'ABC-'开头的最近的<tr> 然后获取特定的<td>元素。

使用HtmlAgilityPack时,您可以选择至少两种方法:

foreach (HtmlNode link in nodes)
{
    //approach 1 : notice dot(.) at the beginning of the XPath
    string xPath1 = 
        @"./parent::tr/preceding-sibling::tr[td[starts-with(.,'ABC-')]][1]/td[starts-with(.,'ABC-')]";
    var n1 = node.SelectSingleNode(xPath1);
    Console.WriteLine(n1.InnerHtml);

    //approach 2 : appending to XPath of current link
    string xPath2 = 
        @"/parent::tr/preceding-sibling::tr[td[starts-with(.,'ABC-')]][1]/td[starts-with(.,'ABC-')]";
    var n2 = node.SelectSingleNode(link.XPath + xPath2);
    Console.WriteLine(n2.InnerHtml);
}

如果您能够使用LINQ-to-XML而不是HAP,那么这可以:

var node = xml.Root.Elements("tr")
    .TakeWhile(tr => !tr.Elements("td")
        .Any(td => td.Value.StartsWith("CONTROLLER2")))
    .SelectMany(tr => tr.Elements("td"))
    .Where(td => td.Value.StartsWith("ABC-"))
    .Last();

我得到了这个结果:

<td>
  <b>ABC-123</b>
</td>

(我检查的是样本中的第二个匹配节点,而不是第一个。)

您可以使用

//tr/td[starts-with(.,'CONTROLLER2')]/(parent::tr/preceding-sibling::tr/td[starts-with(normalize-space(.),'ABC-')])[1]

由于目标节点包含不需要的空间,因此必须使用normalize-space

我认为这样的XPATH(来自当前的CONTROLLER2节点)应该这样做:

string xPath = "../preceding-sibling::tr[starts-with(td , 'ABC-')][1]/td[starts-with(. , 'ABC-')]";

它的意思是

  • 一旦祖先升级就回来(..)
  • 从那里,选择所有前面的兄弟TR元素,其TD元素以'ABC-'开头
  • 得到这些TR的第一个(逆序)。
  • 从这个TR元素中获取以'ABC-'开头的TD元素

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM