2014-09-19 2 views
0

저는 XSLT 프로그래밍의 초보자입니다.XSLT : XML 요소 분할 및 병합

<Test>TestA::test1</Test> 
<Test>TestA::test2</Test> 
<Test>TestB::test3</Test> 
<Test>TestB::test4</Test> 

출력 XML은 다음과 같이한다 : 나는 다음과 같은 XML 변환하는 작업이있어 ​​

<Class id="TestA"> 
    <Method id="test1"/> 
    <Method id="test2"/> 
</Class> 
<Class id="TestB"> 
    <Method id="test3"/> 
    <Method id="test4"/> 
</Class> 

입력 XML은 C++ 스타일 CppUnit을 테스트 케이스의 이름을 포함를 (패턴 클래스 ::방법). 많은 접근법을 시도하고 무수히 많은 stackoverflow threds를 읽었지만 해결책을 찾지 못했습니다.

XSLT 1.0을 사용하여이 문제를 해결해야합니다. 사전에

감사합니다, mexl

이 기본적으로 그룹화 문제, A (아주 약간) 트위스트와 함께 Muenchian grouping에 의해 (XSLT 1.0) 해결 될 것입니다
+1

이것은 * 그룹화 * 문제가 주로를 (수색을하십시오). XSLT 1.0 또는 2.0을 사용하고 있습니까? –

+0

힌트를 보내 주셔서 감사합니다! XSLT 1.0을 사용하여 문제를 해결해야합니다. – mexl916

답변

1

. 그러나 먼저 입력은 루트 요소가 있어야합니다 - 그렇지 않으면 XML 문서 아니다 : 장소에두고

<root> 
    <Test>TestA::test1</Test> 
    <Test>TestA::test2</Test> 
    <Test>TestB::test3</Test> 
    <Test>TestB::test4</Test> 
</root> 

, 다음 스타일 시트 :

XSLT 1.0

<?xml version="1.0" encoding="UTF-8"?> 
<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> 

<xsl:key name="k" match="Test" use="substring-before(., '::')" /> 

<xsl:template match="/"> 
    <output> 
     <xsl:for-each select="root/Test[count(. | key('k', substring-before(., '::'))[1]) = 1]"> 
      <Class id="{substring-before(., '::')}"> 
       <xsl:for-each select="key('k', substring-before(., '::'))"> 
        <Method id="{substring-after(., '::')}"/> 
       </xsl:for-each> 
      </Class> 
     </xsl:for-each> 
    </output> 
</xsl:template> 

</xsl:stylesheet> 

것 반환 :

<?xml version="1.0" encoding="UTF-8"?> 
<output> 
    <Class id="TestA"> 
     <Method id="test1"/> 
     <Method id="test2"/> 
    </Class> 
    <Class id="TestB"> 
     <Method id="test3"/> 
     <Method id="test4"/> 
    </Class> 
</output> 
+0

대단히 고마워요, 마이클! 내 하루를 구 했으니 까 .-) – mexl916

+0

좋은 답변이며, 내 것보다 빨리 제출되었습니다. 이 대답에는 아무런 문제가 없지만,'for-each '대신 중첩 된'apply-templates'를 사용하는 것이 좋습니다. 템플리트 및/또는 입력 데이터가 시간이 지남에 따라 변하면 기꺼이 할 수 있습니다. . 이 접근법에 대한 몇 가지 좋은 점은 http://stackoverflow.com/questions/6342902/for-loops-vs-apply-templates와 'for-each'에 대한 강력한 주장을위한이 블로그를 참조하십시오. http : // gregbee .ch/blog/using-xsl-for-each-is-always-always-wrong – biscuit314

+0

바깥 각이 변경 될 수 있습니다. for-each는 컨텍스트 노드를 변경해야하며 문서가 클 때 성능에 나쁜 "// Test"를 사용하지 않습니다. 좋은 해결책 – ljdelight

0

xsl:key/generate-idsubstring-before /substring-after는,로는 다음과 같습니다

<Tests> 
    <Test>TestA::test1</Test> 
    <Test>TestA::test2</Test> 
    <Test>TestB::test3</Test> 
    <Test>TestB::test4</Test> 
</Tests> 

을 주어 사용이 템플릿 :

<?xml version="1.0" encoding="utf-8"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 

<xsl:output indent="yes" method="xml" encoding="UTF-8"/> 

<xsl:key name="testClassKey" match="Test" use="substring-before(., '::')"/> 

<xsl:template match="Test" mode="asMethod"> 
    <xsl:variable name="methodId" select="substring-after(., '::')" /> 

    <Method id="{$methodId}" /> 
</xsl:template> 


<xsl:template match="Test"> 
    <xsl:variable name="thisTest" select="." /> 
    <xsl:variable name="classId" select="substring-before(., '::')" /> 

    <Class id="{$classId}"> 
    <xsl:apply-templates select="//Test[substring-before(., '::') = $classId]" mode="asMethod"/> 
    </Class> 
</xsl:template> 


<xsl:template match="Tests"> 
    <TestClasses> 
    <xsl:apply-templates select="Test[generate-id(.) = generate-id(key('testClassKey', substring-before(., '::'))[1])]" /> 
    </TestClasses> 
</xsl:template> 
</xsl:stylesheet> 

이 결과를 얻으려면 :

<?xml version="1.0" encoding="UTF-8"?> 
<TestClasses> 
    <Class id="TestA"> 
    <Method id="test1" /> 
    <Method id="test2" /> 
    </Class> 
    <Class id="TestB"> 
    <Method id="test3" /> 
    <Method id="test4" /> 
    </Class> 
</TestClasses>