2014-09-19 142 views
0

我是XSLT編程的初學者。我必須改變以下XML任務:XSLT:拆分和合並XML元素

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

輸出XML應該是這樣的:

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

輸入XML包含的CppUnit測試用例C++風格的名稱(模式類::方法)。 我已經嘗試了很多不同的方法,並閱讀了無數的計算器堆棧,但無法找到解決方案。

我必須解決使用XSLT 1.0的問題。

由於提前, mexl

+1

這主要是*分組*問題(做一個搜索)。你在使用XSLT 1.0還是2.0? – 2014-09-19 14:12:20

+0

謝謝你的提示!我必須使用XSLT 1.0來解決這個問題。 – mexl916 2014-09-19 14:16:16

回答

1

這基本上是一個分組的問題,要由Muenchian grouping解決(在XSLT 1.0)與(非常輕微的)的扭曲。然而,首先你的輸入必須有一個根元素 - 否則它不是一個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 2014-09-19 14:38:43

+0

很好的答案,並提交比我更快。這個答案沒有錯,但我會建議,但使用'apply-templates'而不是嵌套'for-each'當你的模板和/或輸入數據隨時間變化時,你會很高興你做了。有關這兩種方法的一些優點,請參閱此討論:http://stackoverflow.com/questions/6342902/for-loops-vs-apply-templates,並在此博客中反對「for-each」的更強論據。http:// gregbee .ch/blog/using-xsl-for-each-is-almost-always-wrong – biscuit314 2014-09-19 14:46:11

+0

外部for-each可以更改。內部for-each需要更改上下文節點,並避免使用「// Test」,這在文檔很大時對性能不利。好的解決方案 – ljdelight 2014-09-19 15:09:09

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>