2011-11-18 115 views
1

我有一個Word 2003 XML文檔,我試圖在其中搜索某些元素。我已經能夠執行簡單的XPath查詢來查找單個元素,但我很難提出查詢兩個元素之間的搜索:兩個元素之間的XPath

<w:r> 
     <w:fldChar w:fldCharType="begin"/> 
    </w:r> 
    <w:r> 
     <w:instrText> DOCPROPERTY EvidenceBase \* MERGEFORMAT </w:instrText> 
    </w:r> 
    <w:r> 
     <w:fldChar w:fldCharType="separate"/> 
    </w:r> 
    <w:r> 
     <w:t>EvidenceBase</w:t> 
    </w:r> 
    <w:r> 
     <w:fldChar w:fldCharType="end"/> 
    </w:r> 

我正在尋找上述XML,具有AW,R與AW:fldChar它擁有W的屬性:fldCharType用的「開始」值。它應該返回每個元素,直到它碰到一個帶有w:fldChar的w:r,它具有屬性w:fldCharType,值爲「end」。

這可能嗎?

回答

2
//w:r[preceding-sibling::w:r[w:fldChar/@w:fldCharType='begin'] and following-sibling::w:r[w:fldChar/@w:fldCharType='end']] 

請注意,需要將前綴w綁定到XPath表達式名稱空間上下文的正確名稱空間。這是如何完成的取決於你如何使用XPath(XSLT,Java,C#...)。

另外,如果存在多個可能嵌套的「開始」和「結束」標記,則這會更復雜。

+0

這是偉大的,完美的作品! – ScottD

1

在任何類似的問題中,可以使用Kayessian公式爲nodeset交集

如果我們有兩個節點集$ns1$ns2,然後同時屬於這兩個節點集所有節點都通過這個XPath表達式選擇:

$ns1[count(.|$ns2) = count($ns2)] 

你的情況,你有剛剛替補$ns1

//w:r[w:fldChar/@w:fldCharType='begin'][1]/following-sibling::* 

..

和替代$ns2

//w:r[w:fldChar/@w:fldCharType='end'][1]/preceding-sibling::* 

產生的XPath表達式可能看起來太複雜,但你獲得的是非常容易地解決此類問題的能力,幾乎機械:

/*/w:r 
     [w:fldChar/@w:fldCharType='begin']/following-sibling::* 
    [count(. | /*/w:r[w:fldChar/@w:fldCharType='end'] 
            /preceding-sibling::* 
      ) 
    = 
     count(/*/w:r[w:fldChar/@w:fldCharType='end'] 
            /preceding-sibling::*) 
    ] 

基於XSLT的驗證:

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
xmlns:w="some:namespace"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/"> 
    <xsl:copy-of select= 
    "/*/w:r 
      [w:fldChar/@w:fldCharType='begin']/following-sibling::* 
     [count(. | /*/w:r[w:fldChar/@w:fldCharType='end'] 
             /preceding-sibling::* 
       ) 
     = 
      count(/*/w:r[w:fldChar/@w:fldCharType='end'] 
             /preceding-sibling::*) 
     ] 
    "/> 
</xsl:template> 
</xsl:stylesheet> 

當該轉化此XML文檔上施加:

<t xmlns:w="some:namespace"> 
    <w:r> 
     <w:fldChar w:fldCharType="before-begin"/> 
    </w:r> 
    <w:r> 
     <w:fldChar w:fldCharType="begin"/> 
    </w:r> 
    <w:r> 
     <w:instrText> DOCPROPERTY EvidenceBase \* MERGEFORMAT </w:instrText> 
    </w:r> 
    <w:r> 
     <w:fldChar w:fldCharType="separate"/> 
    </w:r> 
    <w:r> 
     <w:t>EvidenceBase</w:t> 
    </w:r> 
    <w:r> 
     <w:fldChar w:fldCharType="end"/> 
    </w:r> 
    <w:r> 
     <w:fldChar w:fldCharType="after-end"/> 
    </w:r> 
</t> 

完全所需元件被選擇和複製到輸出

<w:r xmlns:w="some:namespace"> 
    <w:instrText> DOCPROPERTY EvidenceBase \* MERGEFORMAT </w:instrText> 
</w:r> 
<w:r xmlns:w="some:namespace"> 
    <w:fldChar w:fldCharType="separate"/> 
</w:r> 
<w:r xmlns:w="some:namespace"> 
    <w:t>EvidenceBase</w:t> 
</w:r> 
+0

+1 - 我很欣賞Kayessian公式的優雅,但起初我很驚訝(經過一些*非常非正式的基準測試),它的表現比@G_H的答案中的「天真」解決方案慢得多。 (使用撒克遜的測試。) –

+0

@lwburk:謝謝。是的,這是一個「快速和骯髒」的解決方案,對於大型節點集可能不太有效。無論如何,在XPath 2.0中,'intersect'操作符可能會更有效率。 –

1

如果前述的數目開始是來自不同結束數量,我們必須在開始和結束之間。因此:

w:r[count(preceding-sibling::w:r[w:fldChar/@w:fldCharType='begin']) != count(preceding-sibling::w:r[w:fldChar/@w:fldCharType='end'])]