Xpath,在两个其他节点之间找到节点

时间:2015-08-08 15:33:54

标签: xpath

我需要构建一个通用的XPath来找到正确的节点,其中标准是日期和时间。例如找到“03 May”,“12:17:44”的节点

XML有一个日期和时间标记。 不方便的是,日期标记仅在当天的第一次出现时填充。 (始终填充时间标记)

我试过这个:

/itemisationTable/row[@Date="03 May"]/following-sibling::row[@Time="12:17:44"]

它工作正常,但它不正确,因为这个

/itemisationTable/row[@Date="03 May"]/following-sibling::row[@Time="21:12:06"]

也会找到一个不应该的结果。

我的另一个问题是

/itemisationTable/row[@Date="03 May"]/following-sibling::row[@Time="09:34:13"]

应该找到一个节点,但它不会。

如果有人能帮助我使用XPath,我会很高兴,因为它超过了我的Xpath技能。

这是XML的片段

<itemisationTable index="1" name="Belgium - SMS/Data" total="1.522">
<row Date="03 May" Time="09:34:13" Number="xphone.com" Description="Roaming Data" Origin="Belgium" Destination="Other Provider" InBundle="" Taxable="T" Duration="12.57 MB" Cost_exc_VAT="1.258" />
<row Date="" Time="10:43:41" Number="4428" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="10:43:44" Number="4428" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="12:17:44" Number="4408" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="21:10:50" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="21:11:55" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="04 May" Time="21:12:06" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="21:22:34" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="21:23:23" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="21:23:31" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="05 May" Time="21:23:56" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="21:30:45" Number="4412" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="22:24:35" Number="4431" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="" Time="22:24:38" Number="4431" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
<row Date="06 May" Time="" Number="xphone.com" Description="Roaming Data" Origin="Belgium" Destination="Other Provider" InBundle="" Taxable="T" Duration="2.59 MB" Cost_exc_VAT="0.264" />
<row Date="" Time="07:09:15" Number="4483" Description="xphone SMS" Origin="Belgium" Destination="UK" InBundle="B" Taxable="" Duration="1" Cost_exc_VAT="0.000" />
</itemisationTable>

1 个答案:

答案 0 :(得分:2)

棘手!这不是特别有效,但 是一个可行的解决方案(使用XPath 2.0):

/itemisationTable
  /row[@Date=$date]
  /(self::row | following-sibling::row[
    not(./preceding-sibling::row[@Date != ""][1]/@Date != $date)
  ][not(./@Date != "" and ./@Date != $date)])[@Time=$time]

使用XPath 1.0,这会变得更加毛茸茸:

/itemisationTable
  /row[@Date=$date]
  /following-sibling::row[
    not(./preceding-sibling::row[@Date != ""][1]/@Date != $date)
  ][not(./@Date != "" and ./@Date != $date)][@Time=$time]
| /itemisationTable/row[@Date=$date][not(./@Date != "" and ./@Date !=$date][@Time=$time]

通过XPath引擎设置$date$time - 例如,在XMLStarlet中,这将是--var date='"03 May"';在XQuery引擎中评估XPath,它将与declare variable $date="03 May";等

重要的是使用preceding-sibling回溯并查看您是否越过边界,排除任何真实的节点。

对于具有足够表现力的语言来构建有效的解决方案,我想切换到XQuery。

要允许复制/粘贴测试,已在http://www.freeformatter.com/xpath-tester.html成功使用以下内容:

/itemisationTable   /row[@Date="03 May"]   /following-sibling::row[     not(./preceding-sibling::row[@Date != ""][1]/@Date != "03 May")   ][not(./@Date != "" and ./@Date != "03 May")][@Time="21:11:55"] | /itemisationTable/row[@Date="03 May"][not(./@Date != "" and ./@Date != "03 May")][@Time="21:11:55"]