2010-09-06 72 views
1

林與PHP5的工作,我需要在下面的表格來轉換XML:轉換XML結構到另一個XML結構

<item> 
    <string isNewLine="1" lineNumber="32">some text in new line</string> 
    <string>, more text</string> 
    <item> 
     <string isNewLine="1" lineNumber="33">some text in new line</string> 
     <string isNewLine="1" lineNumber="34">some text</string> 
     <string> in the same line</string> 
     <string isNewLine="1" lineNumber="35">some text in new line</string> 
    </item> 
</item> 

弄成這個樣子:

<item> 
    <line lineNumber="32">some text in new line, more text</string> 
    <item> 
      <line lineNumber="33">some text in new line</string> 
      <line lineNumber="34">some text in the same line</string> 
      <line lineNumber="35">some text in new line</string> 
    </item> 
</item> 

正如你所看到的,它已經加入了包含在多個「字符串」節點中的文本。 另請注意,'串'節點可嵌套在任何級別的其他節點中。

將源xml轉換爲目標xml有什麼可行的解決方案?

謝謝,

+0

檢查評論到接受的答案,XSLT代碼仍然有一個錯誤。 – Tomalak 2010-09-06 15:20:57

+0

好問題(+1)。看到我的答案是唯一正確的解決方案。您接受的解決方案根本不正確 - 只需運行它並將結果與​​您真正想要的結果進行比較即可。 – 2010-09-06 17:37:10

+0

'@ isNewLine'似乎與'@ lineNumber'數據冗餘:如果'string'有一個'@ lineNumber',它總是有一個'@ isNewLine',反之亦然。 – dolmen 2010-09-09 10:21:19

回答

2

這裏是一個有效的,正確的解決方案

<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:key name="knextStrings" 
    match="string[not(@isNewLine)]" 
    use="generate-id(preceding-sibling::string 
           [@isNewLine][1] 
        )"/> 


<xsl:template match="node()|@*"> 
    <xsl:copy> 
    <xsl:apply-templates select="node()|@*"/> 
    </xsl:copy> 
</xsl:template> 

<xsl:template match="string[@isNewLine]"> 
    <line> 
    <xsl:copy-of select="@*[not(name()='isNewLine')]"/> 
    <xsl:copy-of select="text() 
         | 
         key('knextStrings', 
          generate-id() 
          ) 
           /text()"/> 
    </line> 
</xsl:template> 

<xsl:template match="string[not(@isNewLine)]"/> 
</xsl:stylesheet> 

當這種轉變是在最初提供的XML文檔應用

<item> 
    <string isNewLine="1" lineNumber="32">some text in new line</string> 
    <string>, more text</string> 
    <item> 
     <string isNewLine="1" lineNumber="33">some text in new line</string> 
     <string isNewLine="1" lineNumber="34">some text</string> 
     <string> in the same line</string> 
     <string isNewLine="1" lineNumber="35">some text in new line</string> 
    </item> 
</item> 

的希望,正確的結果產生

<item> 
    <line lineNumber="32">some text in new line, more text</line> 
    <item> 
    <line lineNumber="33">some text in new line</line> 
    <line lineNumber="34">some text in the same line</line> 
    <line lineNumber="35">some text in new line</line> 
    </item> 
</item> 
+0

+ 1爲正確的輸出,因爲這正是我的想法;) – 2010-09-06 18:37:17

+0

+1爲了一個有效和正確的解決方案。 – 2010-09-06 18:56:49

+0

該解決方案適用於該框。如果您可以向代碼添加註釋,那將會很棒。 – 2010-09-07 09:10:21

2

這個樣式表生成您正在尋找的輸出:

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

    <!--Identity template simply copies content forward by default --> 
    <xsl:template match="@*|node()"> 
     <xsl:copy> 
      <xsl:apply-templates select="@*|node()"/> 
     </xsl:copy> 
    </xsl:template> 

    <xsl:template match="string[@isNewLine and @lineNumber]"> 
     <line> 
      <xsl:apply-templates select="@*"/> 
      <xsl:apply-templates select="text()" /> 
      <!-- Include the text() from the string elements that come after this element, 
       do not have @isNewLine or @lineNumber, 
       and are only following this particular element --> 
      <xsl:apply-templates select="following-sibling::string[not(@isNewLine and @lineNumber) and generate-id(preceding-sibling::string[1]) = generate-id(current())]/text()" /> 
     </line> 
    </xsl:template> 

    <!--Suppress the string elements that do not contain isNewLine or lineNumber attributes in normal processing--> 
    <xsl:template match="string[not(@isNewLine and @lineNumber)]" /> 

    <!--Empty template to prevent attribute from being copied to output--> 
    <xsl:template match="@isNewLine" /> 

</xsl:stylesheet> 
+0

+1您應該複製除'isNewLine'屬性以外的所有內容。 – Tomalak 2010-09-06 11:58:58

+1

@Mads Hansen:+1正確模式。小編輯:將'string'轉換爲'line'轉換,剝離'@ isNewLine'並減少謂詞。 – 2010-09-06 13:18:19

+0

謝謝@Tomalak,很好。 @Alejandro,我爲@isNewLine添加了一個空模板。對我來說,感覺更清潔一些,以獲得空模板,而不是排除在謂詞過濾器中(一個6,另一個6個)。 – 2010-09-06 13:22:49

0

使用XSL轉換。

PHP documentation

<?php 

$xml = new DOMDocument; 
$xml->load('data.xml'); 

$xsl = new DOMDocument; 
$xsl->load('trans.xsl'); 

$proc = new XSLTProcessor; 
$proc->importStyleSheet($xsl); 

echo $proc->transformToXML($xml); 

?> 

使用迪米特里的答案trans.xsl