2017-05-14 61 views
1

說,我有一個文檔 -如何遍歷內存中的XML結構並替換子項?

<something> 
    <parent> 
    <child>Bird is the word 1.</child> 
    <child>Curd is the word 2.</child> 
    <child>Nerd is the word 3.</child> 
    </parent> 
    <parent> 
    <child>Bird is the word 4.</child> 
    <child>Word is the word 5.</child> 
    <child>Bird is the word 6.</child> 
    </parent> 
</something> 

我想通過文件來遍歷並與「狗」用XQuery和MarkLogic API的替換單詞「鳥」。到目前爲止,我能夠實現與下面的代碼 -

let $doc := $DOC 
    return <something> 
      {for $d at $y in $doc/element() 
      let $p := <parent> 
         {for $c in $d/element() 
         let $child := if(fn:matches($c, "Bird")) then(<child>{fn:replace($c, "Bird", "Dog")}</child>) else($c) 
         return $child 
         }</parent> 
      return $p} 
     </something> 

結果

<something> 
    <parent> 
    <child>Dog is the word 1.</child> 
    <child>Curd is the word 2.</child> 
    <child>Nerd is the word 3.</child> 
    </parent> 
    <parent> 
    <child>Dog is the word 4.</child> 
    <child>Word is the word 5.</child> 
    <child>Dog is the word 6.</child> 
    </parent> 
</something> 

我怎樣才能做到這一點沒有嵌套的for循環?之前曾詢問過這個問題,但是使用了XSLT。

+0

爲什麼不使用像** s/Bird/Dog/g **這樣的正則表達式?它會在一次線性時間內完成。 – Wontonimo

+1

@wontonimo雖然可以對序列化的XML進行字符串操作,但它被認爲是不好的做法。確保您只在實際需要的地方應用更改也更加困難。使用單遍字符串替換時,很難確保只更改'child'元素的內容,而不更改其他元素或屬性的內容。更重要的是,不會有任何混淆XML格式良好的風險,無意中重命名XML標籤,或者更糟糕的是,導致它們被破壞或刪除。 – grtjn

+0

@grtjn - 同意,雖然你可以添加xml標籤檢查到正則表達式像這樣** s /(\> [^ \ <] *)Bird([^ \ <] * \ <)/ $ 1Dog $ 2/g **,如果您檢查將**孩子**更改爲**父**,則會看到它不會修改標籤內部,而只會修改標籤之間的單詞** **。 – Wontonimo

回答

4

編寫一個函數並使用遞歸。隨着typeswitch表達你可以在遞歸的每個階段檢查節點類型,並使用computed element constructor你可以使用一個通用的模板來重建每一個元素,而不知道它的名字:

declare function local:transform(
    $node as node() 
) as node()* 
{ 
    typeswitch ($node) 
    case element() return element { node-name($node) } { 
    $node/@*, 
    for $n in $node/node() 
    return local:transform($n) 
    } 
    case text() return 
    if (matches($node, "Bird")) 
    then text { replace($node, "Bird", "Dog") } 
    else $node 
    default return $node 
}; 

注意,明確地matches檢查是沒有必要的因爲如果沒有任何匹配,replace將返回輸入字符串。

+1

爲document-node()添加一個大小寫,並且在複製元素以實現更好的標識轉換時包含$ node/namespace :: *。 – grtjn

+0

@grtjn是的,我同意,爲了簡潔,我僅僅回答了這個問題。另外,對於性能至關重要的轉換,除非嚴格需要,否則我嘗試排除'namespace :: *',因爲我注意到在MarkLogic中,每個元素的通配符名稱空間軸可能有點顯着,這取決於其大小和內容文件。 – wst

+0

$ node/namespace :: *只應該查看本地聲明。我不得不去嘗試,但如果這對性能有很大影響,我會感到驚訝。但是我會在下次玩這個遊戲時記住它.. – grtjn

4

wst的答案看起來很不錯,但同樣的問題經常被問到,他們創建了一個庫來使這更容易。它通常被稱爲「內存中更新庫」。這方面的一個改進版本可以在這裏找到:

https://github.com/ryanjdew/XQuery-XML-Memory-Operations

我想這可能是價值至少提它..

HTH!