2009-05-14 5 views
9

일부 선택적 노드가있는 기존 XML 문서가 있는데 특정 노드에 새로운 노드를 삽입하려고합니다. 그 결과,기존 문서의 특정 위치에 XML 노드를 삽입하십시오.

<root> 
    <a>...</a> 
    ... 
    <r>...</r> 
    <t>...</t> 
    ... 
    <z>...</z> 
</root> 

새로운 노드 (<s>...</s>)이 노드 <r><t> 사이에 삽입해야합니다 :

문서는 다음과 같이 보이는,

<root> 
    <a>...</a> 
    ... 
    <r>...</r> 
    <s>new node</s> 
    <t>...</t> 
    ... 
    <z>...</z> 
</root> 

문제가 있다는 기존의 노드는 선택 사항입니다. 따라서 XPath를 사용하여 노드 <r>을 찾은 다음 새 노드를 삽입 할 수 없습니다.

나는 "무차별 방식"을 피하고 싶습니다. <r>에서 <a>까지 검색하여 존재하는 노드를 찾으십시오.

또한 XML 문서가 XML 스키마를 준수해야하므로 순서를 유지하려고합니다.

일반 XML 라이브러리뿐만 아니라 XSLT도 사용할 수 있지만 Saxon-B 만 사용하므로 스키마 인식 XSLT 처리는 옵션이 아닙니다.

누구나 그러한 노드를 삽입하는 방법에 대한 아이디어가 있습니까? 당신이 삽입 위치를 찾을 수 정적 경로가 없기 때문에

들으, MyKey_

답변

18

[내 마지막 답변 대체. 지금은 더 나은 당신이 필요로하는 것을 이해]

는 여기에 XSLT 2.0 솔루션입니다 :.

<xsl:stylesheet version="2.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

    <xsl:template match="/root"> 
    <xsl:variable name="elements-after" select="t|u|v|w|x|y|z"/> 
    <xsl:copy> 
     <xsl:copy-of select="* except $elements-after"/> 
     <s>new node</s> 
     <xsl:copy-of select="$elements-after"/> 
    </xsl:copy> 
    </xsl:template> 

</xsl:stylesheet> 

명시 적 후 오는 요소 또는 이전에 오는 요소 중 하나를 나열해야합니다. (두 가지를 모두 나열 할 필요는 없습니다.) 두 목록 중 짧은 것을 선택하는 경향이 있습니다 (위의 예에서 "t"- "z", "a"- "r"대신에).

선택 사양 향상 :

이 일을 얻을 수 있지만, 지금은합니다 (XSLT와 스키마에) 두 개의 서로 다른 장소에서 요소 이름의 목록을 유지해야합니다. 많이 바뀌면 동기화되지 않을 수 있습니다. 새 요소를 스키마에 추가했지만 XSLT에 추가하는 것을 잊어 버린 경우에는 복사되지 않습니다. 걱정이된다면, 당신은 당신 자신의 종류의 스키마 인식을 구현할 수 있습니다.당신이 돈은 지금

<xsl:variable name="elements-after" as="element()*"> 
    <xsl:variable name="root-decl" select="document('root.xsd')/*/xs:element[@name eq 'root']"/> 
    <xsl:variable name="child-decls" select="$root-decl/xs:complexType/xs:sequence/xs:element"/> 
    <xsl:variable name="decls-after" select="$child-decls[preceding-sibling::xs:element[@name eq 's']]"/> 
    <xsl:sequence select="*[local-name() = $decls-after/@name]"/> 
    </xsl:variable> 

이 분명히 더 복잡하지만 :

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> 

    <xs:element name="root"> 
    <xs:complexType> 
     <xs:sequence> 
     <xs:element name="a" type="xs:string"/> 
     <xs:element name="r" type="xs:string"/> 
     <xs:element name="s" type="xs:string"/> 
     <xs:element name="t" type="xs:string"/> 
     <xs:element name="z" type="xs:string"/> 
     </xs:sequence> 
    </xs:complexType> 
    </xs:element> 

</xs:schema> 

이제 당신이해야 할 모든 변수 후 요소-은 $의 당신의 정의를 변경입니다 : 이제 스키마는 다음과 같습니다 가정 해 봅시다 코드에서 "s"이외의 요소를 나열해야합니다. 스크립트의 동작은 스키마를 변경할 때마다 자동으로 업데이트됩니다 (특히 새 요소를 추가하는 경우). 이것이 잔인한 것인지 아닌지 여부는 프로젝트에 따라 다릅니다. 나는 단순히 옵션으로 추가 기능을 제공합니다. :-)

+0

'r'노드가없는 경우에는 원래의 질문에 따라 모든 노드가 선택 사항입니다. 노드가 존재할 수 없다면 템플릿은 어떻게 보이나요? –

+0

죄송합니다. 원래 게시물을 잘못 읽었습니다. 이제 그 대답을 완전히 대체했습니다. 감사. –

+0

정말 멋집니다. 약간의 수정 : $ elments-after를 유도 할 때 's'대신 변수를 사용하면 어떤 자식이든 후에 자동으로 삽입을 처리 할 수 ​​있습니다. – 13ren

0

당신은 무력 검색을 사용해야합니다. 내 접근 방식은 SAX 파서를 사용하여 문서를 읽는 것입니다. 모든 노드는 수정되지 않은 출력으로 복사됩니다.

일반적인 XSLT 도구를 사용할 수없는 이유는 다음과 같은 플래그 sWasWritten이 필요합니다. 변수를 수정할 수있는 곳이 필요합니다.

최대한 빨리 보는 바와 같이 노드>r (t, u, ..., z) 또는 루트 노드의 종료 태그, 나는 sWasWrittentrue이었다 않는 한 s 노드를 작성하고 플래그를 설정 것 sWasWritten .

+0

SAX 처리가 권장대로 작동합니다. 그러나 XSLT는 작업에도 매우 유용합니다 (제 답변 참조). –

0

하는 XPath 솔루션 :

/root/(.|a|r)[position()=last()] 

당신은 명시 적으로 각 노드에 대해 다른 XPath 식을해야합니다 있도록 한 후 삽입 할, 당신이 원하는 하나에 모든 노드를 포함해야 .

/root/(.|a|r|t)[position()=last()] 

참고 앞의 노드 중 어느 것도 존재하지 않는 경우의 특별한 경우 : 예를 들어, <t> (있는 경우) 바로 뒤에 배치하는 그것은 <root>를 반환합니다 ("."). 이것을 확인하고 새로운 노드를 루트 노드의 첫 번째 자식으로 삽입해야합니다 (일반적인 경우). 이것은 그렇게 나쁘지 않습니다. 어쨌든이 특별한 경우를 어떻게 든 처리해야합니다. 이 특수한 경우를 처리하는 또 다른 방법은 다음과 같습니다. 선행 노드가 없으면 0 노드를 반환합니다.

/root/(.|a|r|t)[position()=last() and position()!=1] 

과제 :이 특별한 경우를 처리하는 더 좋은 방법을 찾을 수 있습니까?

관련 문제