需要XPath帮助 - 根据数值范围选择节点

时间:2014-04-22 21:06:57

标签: xml xpath

我很难过 - 如果可能的话,我想使用XPath来解决这个问题,因为它会简化一些支持Java的代码。我的文档结构如下:

<table>
   <row id="a">
      <data-point value="0">5</data-point>
      <data-point value="15">4</data-point>
      <data-point value="30">2</data-point>
      <data-point value="45">0</data-point>
   </row>
   <row id="b">
      <data-point value="0">8</data-point>
      <data-point value="10">6</data-point>
      <data-point value="20">4</data-point>
      <data-point value="30">0</data-point>
   </row>
</table>

选择基于输入值。例如,如果inputValue = 17,我需要选择包含该值的数据点 - 在这种情况下,选择数据点,其中value =&#34; 15&#34;和&#34; 30&#34;。同样,如果值为32,则选择数据点&#34; 30&#34;和&#34; 45&#34;。

如果数字与其中一个数据点完全匹配,则可以只返回匹配的数据点,或者返回数据点和下一个或前一个数据点(只要数据点不重要返回匹配数据点。因此,如果inputValue为15,则可以选择值为&#34; 15&#34;和&#34; 30&#34;,&#34; 0&#34;的数据点。和&#34; 15&#34;或者只是&#34; 15&#34;。

XPath还必须考虑行元素的选择器

我尝试过使用follow-sibbling,last()等进行大量组合,但我似乎无法在合适的XPath上磨练。哪有大师可以拿出一个好的XPath?

以下解决方案已关闭,但在几种情况下仍然失败。以下XPath假定输入选择是针对行id =&#34; a&#34;和inputValue为20:

/table/row[@id='a']/data-point[(20 >= number(@value) or 20 <= number(@value)) and following-sibling::*[1]/@value > 20]

4 个答案:

答案 0 :(得分:2)

您在评论中提到您拥有XPath 2.0,因此您可以使用min和max函数。假设$row是目标行ID,$val是目标值:

/table/row[@id = $row]/data-point[@value = (
   min(../data-point/@value[. ge $val]), max(../data-point/@value[. le $val])
)]

这将找到最低上限和最高下限@value(如果碰巧完全匹配,则可能是相同的)并提取其相应的data-point元素。

即使值在输入中没有按升序排列,这也会起作用。

答案 1 :(得分:0)

您没有指定XPath版本,因此我假设它是1.0。使用纯1.0功能解决将是棘手的,因为没有排序节点集的概念(但肯定有N个XPath专家可以弯曲语言来解决这个问题)。但是,我看到两个选择:

1)以合适的语言编写自定义函数,例如Java,用于获取节点集并返回包含输入值的另一个节点集,或者为null。

2)如果您可以更改输入数据,请应用插入具有目标值的伪<data-point>条目的XSLT,对<row>子项进行排序,然后选择前后兄弟。

答案 2 :(得分:0)

假设$row是您的行项目(在这种情况下为ab)并且$input是您的输入值(在给出的示例中为17) ,以下XPath将获取所需的节点:

(/table/row[@id = $row]/data-point[@value < $input])[last()] | (/table/row[@id = $row]/data-point[@value > $input])[1] | /table/row[@id = $row]/data-point[@value = $input]

它将选择具有低于输入值的值的最后一项和具有高于输入值的第一个值。此外,如果值相等,它将添加节点本身。

答案 3 :(得分:0)

假设您的搜索值存储在$para中,以下XPath表达式应该在XPath 1.0中有效:

data-point[  ( (preceding-sibling::data-point[1]/@value &lt; $para 
                or 
                not(preceding-sibling::data-point[1])
               ) 
              and @value > $para
             ) 
           or 
             ( (following-sibling::data-point[1]/@value > $para 
                or 
                not (following-sibling::data-point[1])
               ) 
               and @value &lt; $para
             )
          ]