【问题标题】:Transform xml structure to another xml structure将 xml 结构转换为另一个 xml 结构
【发布时间】:2011-04-08 17:39:05
【问题描述】:

我正在使用 PHP5,我需要将 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 的可能解决方案是什么?

谢谢,

【问题讨论】:

  • 检查 cmets 到接受的答案,那里的 XSLT 代码仍然存在错误。
  • 好问题 (+1)。请参阅我的答案以获取唯一正确的解决方案。您接受的解决方案根本不正确 - 只需运行它并将结果与​​您真正想要的结果进行比较。

标签: php xml xslt


【解决方案1】:

使用 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);

?>

使用 Dimitri 对trans.xsl 的回答。

【讨论】:

    【解决方案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>
    

    【讨论】:

    • +1 以获得正确的输出,因为这是我的想法;)
    • 此解决方案适用于盒子。如果您可以在代码中添加 cmets,那就太好了。
    【解决方案3】:

    此样式表产生您正在寻找的输出:

    <?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>
    

    【讨论】:

    • @Mads Hansen:+1 对于正确的模式。次要编辑:stringline 转换,剥离 @isNewLine 并减少谓词。
    • 感谢@Tomalak,很好。 @Alejandro,我为@isNewLine 添加了一个空模板。拥有空模板而不是在谓词过滤器中排除对我来说感觉更干净(一个,六个另一个)。
    • @Mads:另外,还有另一个错误:following-sibling::string[…] 应该是 following-sibling::string[…][generate-id(preceding-sibling::string[1]) = generate-id(current())],原因很明显。根据输入,following-sibling::string[1][…] 也可能足够,但这不太干净,因此 OP 必须决定这一点。
    • @Mads Hansen:我认为你的空模板更好。但是你又错过了stringline 的转换。
    • @Mads Hansen:你对使用密钥有什么看法?如:&lt;xsl:key name="kStringByPreceding" match="string[not(@isNewLine)]" use="generate-id(preceding-sibling::string[@isNewLine][1])"/&gt;
    【解决方案4】:

    您应该为此研究一个 XML 解析器。您可以使用基于 SAX 或基于 DOM 的解析器。

    SAX 更高效,但 DOM 可能更适合您的需求,因为它更易于使用。

    【讨论】:

      猜你喜欢
      • 2011-01-17
      • 1970-01-01
      • 1970-01-01
      • 2021-09-06
      • 2023-03-16
      • 1970-01-01
      • 1970-01-01
      • 2014-12-31
      • 2015-02-05
      相关资源
      最近更新 更多