2013-04-12 58 views
1

我必須在「有序」xml文件中進行搜索,其中我的文本到retreive在幾個節點上分散。在多個XML節點中搜索連接文本

<root> 
    <div id="1">Hello</div> 
    <div id="2">Hel</div> 
    <div id="3">lo dude</div> 
    <div id="4">H</div> 
    <div id="5">el</div> 
    <div id="6">lo</div> 
</root> 

搜索必須建立在一個串接文本完成:

HelloHello dudeHello 

但我需要能夠中檢索節點屬性。例如,對於'll'搜索,我希望獲得節點:

<div id="1">Hello</div> 
<div id="2">Hel</div> 
<div id="3">lo dude</div> 
<div id="5">el</div> 
<div id="6">lo</div> 

或至少是id。

有人有一個想法如何在XPath或任何其他方式做到這一點?

我認爲這有點具有挑戰性,我暫時沒有(簡單的)想法。 感謝您的幫助。

編輯:在搜索前必須連接文本是關鍵信息並且必須精確化!

+0

在給定的輸出看,我猜你的搜索令牌實際上是'l'。如果不是,請解釋爲什麼'@ id' 2,3,5,6包含在'll'搜索中。 –

+0

好的,我必須更加精確:搜索前文本必須連接在一起......我要編輯我的問題。 – user2273807

+0

你需要這個解決了_all_搜索令牌或只有這個?一個具體的解決方案很簡單,一般而言相當複雜。你用什麼XPath引擎,你是否受它約束? XQuery也會好嗎? –

回答

0

您的更新要求使問題變得更加複雜,因爲「元素換行」可能發生在搜索標記內的任意點處,甚至可能跨越多個元素。我認爲您不能在XPath < 3.0中編寫查詢(如果您只能在XPath中執行此操作)。我使用了XQuery,它擴展了XPath。該代碼在BaseX中運行良好,但也應該在所有其他XQuery引擎中運行(可能需要XQuery 3.0,沒有看過)。

代碼變得相當複雜,我想我在那裏寫足夠的評論使其易於理解。它要求節點位於下一個元素的內部,但通過微小的調整,它也可以用來遍歷任意的XML結構(想想HTML和<span/>和其他標記)。

(: functx dependencies :) 
declare namespace functx = "http://www.functx.com"; 
declare function functx:is-node-in-sequence 
    ($node as node()? , 
    $seq as node()*) as xs:boolean { 

    some $nodeInSeq in $seq satisfies $nodeInSeq is $node 
} ; 
declare function functx:distinct-nodes 
    ($nodes as node()*) as node()* { 

    for $seq in (1 to count($nodes)) 
    return $nodes[$seq][not(functx:is-node-in-sequence(
           .,$nodes[position() < $seq]))] 
} ; 

declare function local:search($elements as item()*, $pattern as xs:string) as item()* { 
    functx:distinct-nodes(
    for $element in $elements 
    return ($element[contains(./text(), $pattern)], local:start-search($element, $pattern)) 
) 
}; 

declare function local:start-search($element as item(), $pattern as xs:string) as item()* { 
    let $splits := (
     (: all possible prefixes of search token :) 
     for $i in 1 to string-length($pattern) - 1 
     (: check whether element text starts with prefix :) 
     where ends-with($element/text(), substring($pattern, 1, $i)) 
     return $i 
    ) 
    (: go on for all matching prefixes :) 
    for $split in $splits 
    return 
     (: recursive call to next element :) 
     let $continue := local:continue-search($element/following-sibling::*[1], substring($pattern, $split+1)) 
     where not(empty($continue)) 
     return ($element, $continue) 
}; 

declare function local:continue-search($element as item()*, $pattern as xs:string) as item()* { 
    if (empty($element)) then() else 
    (: case a) text node contains whole remaining token :) 
    if (starts-with($element/text(), $pattern)) 
    then ($element) 
    (: case b) text node is part of token :) 
    else if (starts-with($pattern, $element/text())) 
    then 
    (: recursive call to next element :) 
    let $continue := local:continue-search($element/following-sibling::*[1], substring($pattern, 1+string-length($element/text()))) 
    where not(empty($continue)) 
    return ($element, $continue) 
    (: token not found :) 
    else() 
}; 

let $token := 'll' 
return local:search(//div, $token) 
+0

剛看到你的答案,非常感謝!我現在要試試這個。哇!看起來很複雜,但問題是! – user2273807

0

在XPath 2,您可以使用令牌化計算如何經常搜索的文本出現,然後測試每個節點,如果不包括在文本中這個節點上,減少出現的次數。如果數量減少,則該節點必須包含在結果中。這並非如此之快。

假設只有在直接子文本節點的問題,比如上例中,它看起來像這樣:

for $searched in "ll" 
return //*/ for $matches in count(tokenize(string-join(*, ""), $searched)) - 1 
      return *[$matches > count(tokenize(concat(" ",string-join(preceding-sibling::*, "")), $searched)) + 
           count(tokenize(concat(" ",string-join(following-sibling::*, "")), $searched)) - 2] 
+0

感謝您的回答。我不確定它可以適用於任何搜索模式,但我必須承認我不習慣XPath,我必須嘗試查看... – user2273807