2011-02-25 61 views
5

我有XML這是structed喜歡使用XSLT genric因爲我有許多XML轉換爲這種格式下Hierarchial XML來平整的XML使用XSLT

<root> 
    <PNode> 
    <node1> 
     <node1Child>data</node1Child> 
     <node2Child>data</node2Child> 
    </node1> 
    </PNode> 
    <SecondNode> 
    <node1> 
     <node1Child> 
     <child>data</child> 
     </node1Child> 
    </node1> 
    </SecondNode> 
</root> 

我想輸出。

<root> 
    <Pnode-node1-node1Child>data</Pnode-node1-node1Child> 
    <Pnode-node1-node2Child>data</Pnode-node1-node2Child> 
    <SecondNode-node1-node1child-child>data</SecondNode-node1-node1child-child> 
</root> 

它可能會更深或更少。
我能做到這一點通過XSLT請提供任何例子或引用

我想這樣做產生從SQL Server 2K8 R2 RDL PDF。因爲rdl不接受嵌套的xml,所以需要將它變平。

+0

想你的意思是數據 JasonPlutext 2011-02-25 07:47:50

+0

問得好,+1。查看我的答案,獲得完整,簡短和簡單的解決方案。還提供了廣泛的解釋。該轉換是健壯的,並將在名稱空間和前綴名稱的情況下工作。 – 2011-02-25 13:57:20

回答

3

這個樣式表:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="/*"> 
     <xsl:copy> 
      <xsl:apply-templates/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*"> 
     <xsl:param name="pName"/> 
     <xsl:apply-templates> 
      <xsl:with-param name="pName" select="concat($pName,name(),'-')"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="text()"> 
     <xsl:param name="pName"/> 
     <xsl:element name="{substring($pName,1,string-length($pName)-1)}"> 
      <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

輸出:

<root> 
    <PNode-node1-node1Child>data</PNode-node1-node1Child> 
    <PNode-node1-node2Child>data</PNode-node1-node2Child> 
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child> 
</root> 

更新:如果有可能empy節點...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
    <xsl:strip-space elements="*"/> 
    <xsl:template match="/*"> 
     <xsl:copy> 
      <xsl:apply-templates/> 
     </xsl:copy> 
    </xsl:template> 
    <xsl:template match="*"> 
     <xsl:param name="pName"/> 
     <xsl:apply-templates> 
      <xsl:with-param name="pName" select="concat($pName,name(),'-')"/> 
     </xsl:apply-templates> 
    </xsl:template> 
    <xsl:template match="*[not(*)]"> 
     <xsl:param name="pName"/> 
     <xsl:element name="{$pName}{name()}"> 
      <xsl:value-of select="."/> 
     </xsl:element> 
    </xsl:template> 
</xsl:stylesheet> 

注意:匹配最裏面的元素。

+0

+1比我的好多了。 – 2011-02-25 14:48:44

+0

+1以獲得正確的解決方案。 – Flack 2011-02-25 18:19:02

+0

@Ajjandro小和完美的答案。萬分感謝。 – Techmaster 2011-02-27 11:34:52

0

在你的模板中,測試一個子節點。

如果存在子節點,則將它的前一個參數值與該元素的名稱連接起來。

如果只有#text,輸出使用該參數作爲其名稱的新元件,並設置其內容到#text

+0

@Alex Nikolaenkov這不是給我節點。輸出數據元數據 – Techmaster 2011-02-25 10:01:23

+0

@Jagan,如果您使用瀏覽器查看結果,請使用'View Source'來檢查xml文檔。我已經使用Xalan XSLT處理器驗證了我的樣式表。 – 2011-02-25 10:17:05

1

鑑於這種輸入:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <PNode> 
     <node1> 
      <node1Child>data</node1Child> 
      <node2Child>data</node2Child> 
     </node1> 
    </PNode> 
    <SecondNode> 
     <node1> 
      <node1Child> 
       <child>data</child> 
      </node1Child> 
     </node1> 
    </SecondNode> 
</root> 

以下樣式表:

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

    <xsl:strip-space elements="*"/> 
    <xsl:output indent="yes" method="xml"/> 

    <xsl:template match="root"> 
     <xsl:copy> 
      <xsl:apply-templates select="*" mode="flatten"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="*[normalize-space(text())]" mode="flatten"> 
     <xsl:param name="name-prefix" select="''"/> 
     <xsl:variable name="name"> 
      <xsl:call-template name="construct-name"> 
       <xsl:with-param name="name-prefix" select="$name-prefix"/> 
      </xsl:call-template> 
     </xsl:variable> 

     <xsl:element name="{$name}"> 
      <xsl:apply-templates select="text()"/> 
     </xsl:element> 

     <xsl:apply-templates select="node()" mode="flatten"> 
      <xsl:with-param name="name-prefix" select="$name"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template match="*[not(normalize-space(text()))]" mode="flatten"> 
     <xsl:param name="name-prefix" select="''"/> 

     <xsl:variable name="prefix"> 
      <xsl:call-template name="construct-name"> 
       <xsl:with-param name="name-prefix" select="$name-prefix"/> 
      </xsl:call-template> 
     </xsl:variable> 

     <xsl:apply-templates select="node()" mode="flatten"> 
      <xsl:with-param name="name-prefix" select="$prefix"/> 
     </xsl:apply-templates> 
    </xsl:template> 

    <xsl:template name="construct-name"> 
     <xsl:param name="name-prefix"/> 
     <xsl:choose> 
      <xsl:when test="$name-prefix"> 
       <xsl:value-of select="concat($name-prefix, '-', local-name(.))"/> 
      </xsl:when> 
      <xsl:otherwise> 
       <xsl:value-of select="local-name(.)"/> 
      </xsl:otherwise> 
     </xsl:choose> 
    </xsl:template> 

    <xsl:template match="text()" mode="flatten"/> 
</xsl:stylesheet> 

主要生產通緝的結果:

<?xml version="1.0" encoding="UTF-8"?> 
<root> 
    <PNode-node1-node1Child>data</PNode-node1-node1Child> 
    <PNode-node1-node2Child>data</PNode-node1-node2Child> 
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child> 
</root> 
+0

嘿亞歷克斯它不工作我試圖在xml間諜,手寫筆stuio和通過C#代碼。它只給出數據節點根本就沒有。我剛剛拿到了數據元數據。請重新檢查它。 – Techmaster 2011-02-25 10:57:20

+0

@Techmaster,請指定您的'XSLT處理器'。 – 2011-02-25 11:12:46

+0

我正在使用C#中的代碼處理它。我也嘗試使用XML間諜工具。所有僅提供數據。謝謝。 – Techmaster 2011-02-25 11:36:49

3

這種轉變

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
<xsl:output omit-xml-declaration="yes" indent="yes"/> 
<xsl:strip-space elements="*"/> 

<xsl:template match="/*"> 
    <root> 
     <xsl:apply-templates/> 
    </root> 
</xsl:template> 

<xsl:template match="text()"> 
    <xsl:variable name="vCompName"> 
    <xsl:for-each select="ancestor::*[not(position() =last())]"> 
    <xsl:value-of select="translate(name(), ':', '_')"/> 
    <xsl:if test="not(position()=last())">-</xsl:if> 
    </xsl:for-each> 
    </xsl:variable> 

    <xsl:element name="{$vCompName}"> 
    <xsl:value-of select="."/> 
    </xsl:element> 
</xsl:template> 
</xsl:stylesheet> 

時所提供的XML文檔應用:

<root> 
    <PNode> 
     <node1> 
      <node1Child>data</node1Child> 
      <node2Child>data</node2Child> 
     </node1> 
    </PNode> 
    <SecondNode> 
     <node1> 
      <node1Child> 
       <child>data</child> 
      </node1Child> 
     </node1> 
    </SecondNode> 
</root> 

產生想要的,正確的結果:

<root> 
    <PNode-node1-node1Child>data</PNode-node1-node1Child> 
    <PNode-node1-node2Child>data</PNode-node1-node2Child> 
    <SecondNode-node1-node1Child-child>data</SecondNode-node1-node1Child-child> 
</root> 

說明

  1. 而且在頂root包裝元素在文檔中,只有一個單一的模板。它匹配任何非空白文本節點。

  2. 除了在文檔順序的第一個(它是最後在反向軸ancestor::)所有元素的祖先的節點集是串接用'-'字符和一個元件被構造爲具有此字符串爲名稱。

  3. 在上面2.中的字符串連接操作之前,每個名稱都被修改,以便其中任何':'字符被下劃線字符替代。如果某些名稱中包含名稱空間前綴,這將使轉換不會生成無效的化合物名稱。

  4. 最後,將當前文本節點複製爲動態構造元素的子元素。

+0

+1爲一個很好的答案。順便說一句,爲什麼你的'if test' expr被包裹在一個額外的括號裏? – Flack 2011-02-25 18:18:35

+0

@Flack:謝謝。至於額外的括號:我開始把它寫成一個C#'if'語句,然後注意到我正在做什麼,並把它放在一個'test'屬性裏面:)。將簡化它。 – 2011-02-25 18:24:03