2010-02-17 203 views
4

我有一個問題。我有以下源XML文件:使用xslt將xml結構轉換爲另一個xml結構

源XML:

<Container> 
    <DataHeader> 
    <c id="b" value="TAG" /> 
    <c id="g" value="Info" /> 
    </DataHeader> 
    <Data> 
    <Rows> 
     <r no="1"> 
     <c id="b" value="uid1" uid="T.A.uid1" /> 
     <c id="g" value="uid1|tag1|attr1|somevalue1" /> 
     </r> 
    <r no="1"> 
     <c id="b" value="uid1" uid="T.A.uid1" /> 
     <c id="g" value="uid1|tag1|attr2|somevalue2" /> 
     </r> 
     <r no="2"> 
     <c id="b" value="uid1" uid="T.A.uid1" /> 
     <c id="g" value="uid1|tag2|attr3|somevalue3" /> 
     </r> 
    <r no="10"> 
     <c id="b" value="uid2" uid="T.A.uid2" /> 
     <c id="g" value="uid2|tag1|attr1|somevalue4" /> 
     </r> 
     <r no="11"> 
     <c id="b" value="uid2" uid="T.A.uid2" /> 
     <c id="g" value="uid2|tag2|attr3|somevalue5" /> 
     </r> 
    </Rows> 
    </Data> 
</Container> 

元素 'ç' id爲 '' 是在源XML重要。這是一個連接的字符串,其值由'|'。我們需要這個值來製作目標xml。 元素'c'與id'b''您可以用來分隔'uid'。值的

示例和explantion:

<c id="g" value="uid1|tag1|attr1|somevalue1" /> 
**uid value** | element node | **attribute** | attribute value 
**uid1** | tag1 | **attr1** |somevalue1 

的Al元素與相同的 'UID' 具有要被彙集到1張 「TestTag」 元件(見目標XML)。 需要將相同父元素(例如'tag1')的屬性(attr1,attr2)添加到1個元素。 我只能使用xslt(xpath)1.0。

轉換後目標xml文件應該看起來像這樣。通過XSL轉換後

目標XML:

<Container> 
<TestTag> 
    <object UID="T.A.uid1" Name="uid1"/> 
    <tag1 attr1="somevalue1" attr2="somevalue2"/> 
    <tag2 attr3="*somevalue3"/> 
</TestTag> 
<TestTag> 
    <Iobject UID="T.A.uid2" Name="uid2"/> 
    <tag1 attr1="somevalue4" /> 
    <tag2 attr3="somevalue5"/> 
</TestTag> 
</Container> 

什麼是轉換XML源到目標XML可能的解決方案?我嘗試了幾件事,但我現在被卡住了。

+0

+1很好的第一次問題。歡迎來到堆棧溢出! – Tomalak 2010-02-17 17:11:37

回答

2

這不正是困難,但令人難以置信的是由於大量的(但必要的)嵌套使用substring-before()substring-after()

<xsl:stylesheet 
    version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
> 
    <!-- index <c> nodes by their @id + "uid value" --> 
    <xsl:key name="kObject" match="r/c" use=" 
    concat(@id, '|', @value) 
    " /> 
    <!-- index <c> nodes by their @id + "uid value" --> 
    <xsl:key name="kTagByUid" match="r/c" use=" 
    concat(@id, '|', substring-before(@value, '|')) 
    " /> 
    <!-- index <c> nodes by their @id + "uid value" + "tag name" --> 
    <xsl:key name="kTagByName" match="r/c" use=" 
    concat(@id, '|', 
     substring-before(
     @value, 
     substring-after(substring-after(@value, '|'), '|') 
    ) 
    ) 
    " /> 

    <xsl:variable name="vTagId" select="/Container/DataHeader/c[@value='TAG'][1]/@id" /> 
    <xsl:variable name="vInfoId" select="/Container/DataHeader/c[@value='Info'][1]/@id" /> 

    <!-- processing starts here --> 
    <xsl:template match="Container"> 
    <xsl:copy> 
     <!-- apply templates to unique <c @id=$vTagId> tags --> 
     <xsl:apply-templates mode="tag" select=" 
     Data/Rows/r/c[@id=$vTagId][ 
      generate-id() 
      = 
      generate-id(key('kObject', concat(@id, '|', @value))[1]) 
     ] 
     " /> 
    </xsl:copy> 
    </xsl:template> 

    <xsl:template match="c" mode="tag"> 
    <TestTag> 
     <object UID="{@uid}" name="{@value}" /> 
     <!-- apply templates to unique <c @id="g"> tags --> 
     <xsl:apply-templates mode="info" select=" 
     key('kTagByUid', concat($vInfoId, '|', @value))[ 
      generate-id() 
      = 
      generate-id(
      key(
       'kTagByName', 
       concat(@id, '|', 
       substring-before(
        @value, 
        substring-after(substring-after(@value, '|'), '|') 
       ) 
      ) 
      )[1] 
     ) 
     ] 
     " /> 
    </TestTag> 
    </xsl:template> 

    <xsl:template match="c" mode="info"> 
    <!-- select 'uid1|tag1|' - it's the key to kTagByName --> 
    <xsl:variable name="key" select="substring-before(@value, substring-after(substring-after(@value, '|'), '|'))" /> 
    <!-- select 'tag1' - it's the element name --> 
    <xsl:variable name="name" select="substring-before(substring-after($key, '|'), '|')" /> 

    <xsl:element name="{$name}"> 
     <xsl:for-each select="key('kTagByName', concat(@id, '|', $key))"> 
     <!-- select 'attr1|somevalue1' - it's the attribute definition --> 
     <xsl:variable name="attrDef" select="substring-after(@value, $key)" /> 
     <!-- create an attribute --> 
     <xsl:attribute name="{substring-before($attrDef, '|')}"> 
      <xsl:value-of select="substring-after($attrDef, '|')" /> 
     </xsl:attribute> 
     </xsl:for-each> 
    </xsl:element> 
    </xsl:template> 

</xsl:stylesheet> 

產生:

<Container> 
    <TestTag> 
    <object UID="T.A.uid1" name="uid1" /> 
    <tag1 attr1="somevalue1" attr2="somevalue2"></tag1> 
    <tag2 attr3="somevalue3"></tag2> 
    </TestTag> 
    <TestTag> 
    <object UID="T.A.uid2" name="uid2" /> 
    <tag1 attr1="somevalue4"></tag1> 
    <tag2 attr3="somevalue5"></tag2> 
    </TestTag> 
</Container> 

注意,這並不注意重複屬性定義。如果您碰巧有uid1|tag1|attr1|somevalue1及更高版本uid1|tag1|attr1|othervalue1,那麼您將最終得到一個屬性:attr1="othervalue1",因爲<xsl:for-each>都會輪到他們,而後者會贏(即在輸出中結束)。

也可以迎合這一點,它將需要一個更多的關鍵和一個Muenchian分組,我將把它作爲讀者的練習。嘿。 ;)

+0

+1,很好的答案[15個字符] – 2010-02-18 04:07:14

+0

Hello Tomalak, 感謝您的幫助。您的解決方案效果非常好。 但現在我有另一個問題。 在源xml中還有一個DataHeader元素。我們不希望匹配id的值,因爲這是生成的,組合ID和值可能每次都是不同的。 我們的解決方案是使用xsl:key 按值獲取id。 我做了兩個變量來獲取id('b'和'g')的值。例如: TripleJ 2010-02-19 09:33:41

+0

問題是,在您的示例中,您在xsl:key中的id匹配模板匹配,我不能在xsl:key或模板匹配中使用變量。 是不可能的。我們如何通過使用上面的解決方案獲得相同的結果,或者我們必須重寫整個xslt才能使其工作? – TripleJ 2010-02-19 09:33:58