用于检索最旧/最早节点的Xpath表达式

时间:2010-06-09 15:34:45

标签: xml xpath xquery msxml msxml6

我有一个XML代码段,所以:

<STATES>
  <STATE>
    <NAME>Alabama</NAME>
    <ABBREVIATION>AL</ABBREVIATION>
    <CAPITAL>Montgomery</CAPITAL>
    <POPULATION>4661900</POPULATION>
    <AREA>52419</AREA>
    <DATEOFSTATEHOOD>14 December 1819</DATEOFSTATEHOOD>
  </STATE>
  <STATE>
    <NAME>Alaska</NAME>
    <ABBREVIATION>AK</ABBREVIATION>
    <CAPITAL>Juneau</CAPITAL>
    <POPULATION>698473</POPULATION>
    <AREA>663268</AREA>
    <DATEOFSTATEHOOD>1 January 1959</DATEOFSTATEHOOD>
  </STATE>
  <STATE>
    <NAME>Delaware</NAME>
    <ABBREVIATION>DE</ABBREVIATION>
    <CAPITAL>Dover</CAPITAL>
    <POPULATION>885122</POPULATION>
    <AREA>2490</AREA>
    <DATEOFSTATEHOOD>7 December 1787</DATEOFSTATEHOOD>
  </STATE>
</STATES>
<etc, etc.>

我想检索(例如)最古老州的首府(即“Dover”)。 我设法做到这一点:

//STATES/STATE[DATEOFSTATEHOOD='7 December 1787']/CAPITAL/text()

但无法弄清楚怎么说'DATEOFSTATEHOOD = {最早的DATEOFSTATEHOOD}'。

有人能指出我正确的方向吗?

解决方案:马特的解决方案或多或少都是现货。我不得不重新格式化日期(我使用了YYYYMMDDD),因为正如所指出的,Xpath 1.0不支持我使用的日期格式。此外,Microsoft的XML库(4.0和6.0)使用Matt的表达式返回整个节点列表。反转测试修复了该问题,使其仅返回最早的节点。

所以:

//STATES/STATE[(DATEOFSTATEHOOD < //STATES/STATE/DATEOFSTATEHOOD)]/CAPITAL/text()

3 个答案:

答案 0 :(得分:3)

XPATH 1.0不支持您提供的格式的日期。如果您能够使用这些日期的数字表示,例如17871207,那么您可以轻松地这样做:

//STATES/STATE[not(DATEOFSTATEHOOD > //STATES/STATE/DATEOFSTATEHOOD)]/CAPITAL/text()

如果这不可行,则可能值得尝试将DATEOFSTATEHOOD节点格式化为xs:date并执行相同的操作:

//STATES/STATE[not(xs:date(DATEOFSTATEHOOD) > xs:date(//STATES/STATE/DATEOFSTATEHOOD))]/CAPITAL/text()

语法可能不完全正确,但希望它能让你开始。

答案 1 :(得分:1)

你能把它们重新格式化为xs:dates?

let $dates := (xs:date('2000-10-23'), xs:date('1999-12-26'))
let $min := fn:min($dates)
let $max := fn:max($dates)
return $min

在MarkLogic Server中完成,但我认为这都是标准的东西。

答案 2 :(得分:1)

您可以使用XQuery重新格式化日期,并使用min()找到最早的日期:

declare variable $monthnames := ("January","February","March","April","May","June","July","August","September","October","November","December");

declare function local:pad-zero($s as xs:string) as xs:string {
  if (string-length($s) = 1) then concat("0",$s) else $s
};

declare function local:df ($d as xs:string) as xs:date {
  let $dp := tokenize($d," ")
  let $year := $dp[3]
  let $month := local:pad-zero(string(index-of($monthnames,$dp[2])))
  let $day := local:pad-zero($dp[1])
  return
    concat($year,"-",$month,"-",$day)


};

let $states := 
<STATES>
  <STATE>
    <NAME>Alabama</NAME>
    <ABBREVIATION>AL</ABBREVIATION>
    <CAPITAL>Montgomery</CAPITAL>
    <POPULATION>4661900</POPULATION>
    <AREA>52419</AREA>
    <DATEOFSTATEHOOD>14 December 1819</DATEOFSTATEHOOD>
  </STATE>
  <STATE>
    <NAME>Alaska</NAME>
    <ABBREVIATION>AK</ABBREVIATION>
    <CAPITAL>Juneau</CAPITAL>
    <POPULATION>698473</POPULATION>
    <AREA>663268</AREA>
    <DATEOFSTATEHOOD>1 January 1959</DATEOFSTATEHOOD>
  </STATE>
  <STATE>
    <NAME>Delaware</NAME>
    <ABBREVIATION>DE</ABBREVIATION>
    <CAPITAL>Dover</CAPITAL>
    <POPULATION>885122</POPULATION>
    <AREA>2490</AREA>
    <DATEOFSTATEHOOD>7 December 1787</DATEOFSTATEHOOD>
  </STATE>
</STATES>


return 
   $states//STATE
     [local:df(DATEOFSTATEHOOD) = 
      min($states//STATE/local:df(DATEOFSTATEHOOD))
     ]

您可以在eXist sandbox

中执行此操作