2017-05-14 6 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의와 단어 "새"를 대체하고자하는 문서 수준을 가지고있다. 지금까지 나는

<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> 

어떻게 루프를 중첩하지 않고 달성 할 수있는 다음과 같은 코드 -

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> 

결과로 달성 할 수 있습니다 무엇입니까? 이 질문은 이전에 XSLT를 사용하여 요청되었습니다.

+0

** s/Bird/Dog/g **와 같은 정규 표현식을 사용하지 않는 이유는 무엇입니까? 단일 패스에서 선형 시간으로 완료됩니다. – Wontonimo

+1

@wontonimo 직렬화 된 XML에서 문자열 조작을 수행하는 것이 가능하지만 나쁜 관행으로 간주됩니다. 또한 실제로 필요한 곳에서만 변경 사항을 적용하는 것이 훨씬 더 어렵습니다. 단일 패스 문자열을 사용하면 다른 요소 또는 속성의 내용이 아닌'child' 요소의 내용 만 변경하는 것이 어렵습니다. 더 중요한 것은 실수로 XML 태그의 이름을 바꾸거나 악화되어 XML 태그가 손상되거나 제거되는 등 XML의 올바른 형식을 엉망으로 만들 위험이 없다는 것입니다. – grtjn

+0

@grtjn - xml 태그를 정규식에 추가 할 수 있지만 ** **/(\> [^ \ <] *) Bird ([^ \ <] * \ <)/$ 1Dog $ 2/g ** ** ** ** ** ** ** ** ** child **를 확인하면 태그 내부에서 수정되지 않고 태그 사이에 ** child ** 단어 만 수정된다는 것을 알 수 있습니다. – Wontonimo

답변

4

함수를 작성하고 재귀를 사용하십시오. typeswitch 표현하면 재귀의 각 단계에서 노드 유형을 확인할 수 있으며, computed element constructor 당신이 일반적인 템플릿을 사용할 수 있습니다 사용하는 이름 모른 채 모든 요소를 ​​다시 명시 적으로 matches으로 확인

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 
}; 

주의 할 필요가 없습니다 일치하지 않는 경우 replace이 입력 문자열을 반환하기 때문입니다.

+1

은 document-node()에 대/소문자를 추가하고보다 나은 신원 변환을 위해 요소를 복사 할 때 $ node/namespace :: *를 포함합니다. – grtjn

+0

@ grtjn 예, 동의했습니다. 질문에 대답하기 위해 간단하게하기 위해 제외했습니다. 또한 성능이 중요한 변환의 경우 MarkLogic에서 네임 스페이스 축의 와일드 카드는 모든 요소에서 네임 스페이스 축의 크기를 다소 크게 할 수 있다는 것을 알아 냈으므로 엄격히 필요하지 않으면 네임 스페이스 :: *를 제외하려고합니다. 문서. – wst

+0

$ node/namespace :: *는 로컬 선언 만 살펴야합니다. 시도해야하지만 성능에 큰 영향을 미친다면 놀랄 것입니다. 하지만 다음 번에 그걸로 놀아 보겠다. .. – grtjn

4

wst의 답변은 매우 좋아 보이지만 같은 질문에 자주 묻습니다. 더 쉽게 만들 수있는 라이브러리를 만들었습니다. 흔히 '메모리 내 업데이트 라이브러리'라고합니다. 그것의 개선 된 버전은 여기에서 찾을 수 있습니다 : https://github.com/ryanjdew/XQuery-XML-Memory-Operations

내가 그것을 적어도 그것을 언급 할 가치가 될 줄 알았는데 ..

HTH를!