2011-11-18 77 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'])]