php - 包装一组 XML 节点

标签 php xml xslt

我正在使用 PHP5,我需要将 XML 转换为以下形式:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <anotherNode>some text</anotherNode
        <item label="a">some text</item>
        <item label="b">some text</item>          
    </item>
</list>

变成这样:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <anotherNode>some text</anotherNode>
        <list> <!-- opening new wrapper node-->
            <item label="a">some text</item>
            <item label="b">some text</item>
        </list> <!-- closing new wrapper node-->
    </item>
</list> 

正如您在上面看到的,我需要向任何尚未被“列表”节点包装的“项目”节点添加一个包装器节点。

将源 xml 转换为目标 xml 的可能解决方案是什么?

更新:

注1:任意一个或一组<item>节点需要用 <list> 包裹节点,如果它还没有包装。

注2:需要保持内容的顺序。

注3: 如果有<item> <anotherNode> 之前和之后的节点. 它应该改变这个:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <item label="a">some text</item>
        <item label="b">some text</item>          
        <anotherNode>some text</anotherNode>
        <item label="c">some text</item>
        <item label="d">some text</item>          
    </item>
</list>

进入这个:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <list> <!-- opening new wrapper node-->
            <item label="a">some text</item>
            <item label="b">some text</item>          
        </list> <!-- closing new wrapper node-->
        <anotherNode>some text</anotherNode>
        <list> <!-- opening new wrapper node-->
            <item label="c">some text</item>
            <item label="d">some text</item>
        </list> <!-- closing new wrapper node-->
    </item>
</list>

谢谢,

最佳答案

这个转换:

<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="node()|@*" name="identity">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="item/item[1]">
  <list>
   <xsl:apply-templates mode="copy"
    select=".| following-sibling::item"/>
  </list>
 </xsl:template>

 <xsl:template match="item" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>

 <xsl:template match="item/item[not(position()=1)]"/>
</xsl:stylesheet>

应用于提供的 XML 文档时:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <anotherNode>some text</anotherNode>
        <item label="a">some text</item>
        <item label="b">some text</item>
    </item>
</list>

产生想要的、正确的结果:

<list>
   <item label="(1)">some text</item>
   <item label="(2)">
      <anotherNode>some text</anotherNode>
      <list>
         <item label="a">some text</item>
         <item label="b">some text</item>
      </list>
   </item>
</list>

请注意:

  1. 身份规则的使用和覆盖

  2. 抑制某些元素。

  3. 使用不同的模式处理某些元素。

更新:

OP 添加了额外的要求:

"如果在 anothernode 之前和之后有 item 元素,则必须包含每个这样的 item 元素组在单独的列表中”

<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="kfollnonitem" match="item"
  use="generate-id(preceding-sibling::*[not(self::item)][1])"/>

 <xsl:key name="kprecnonitem" match="item"
  use="generate-id(following-sibling::*[not(self::item)][1])"/>

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

 <xsl:template match="*[not(self::list)]/item[1]">
  <list>
   <xsl:apply-templates mode="copy"
    select="key('kprecnonitem',
                 generate-id(following-sibling::*[not(self::item)][1])
                 )"/>
  </list>
 </xsl:template>

 <xsl:template match=
  "*[not(self::list) and item]/*[not(self::item)]">
  <xsl:call-template name="identity"/>

  <list>
    <xsl:apply-templates mode="copy"
     select="key('kfollnonitem', generate-id())"/>
  </list>
 </xsl:template>

 <xsl:template match="item" mode="copy">
  <xsl:call-template name="identity"/>
 </xsl:template>

 <xsl:template match="item/item[not(position()=1)]"/>
</xsl:stylesheet>

当对以下 XML 文档执行此转换时:

<list>
    <item label="(1)">some text</item>
    <item label="(2)">
        <item label="a">some text</item>
        <item label="b">some text</item>
        <anotherNode>some text</anotherNode>
        <item label="c">some text</item>
        <item label="d">some text</item>
    </item>
</list>

产生了想要的、正确的结果:

<list>
   <item label="(1)">some text</item>
   <item label="(2)">
      <list>
         <item label="a">some text</item>
         <item label="b">some text</item>
      </list>
      <anotherNode>some text</anotherNode>
      <list>
         <item label="c">some text</item>
         <item label="d">some text</item>
      </list>
   </item>
</list>

关于php - 包装一组 XML 节点,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3652194/

相关文章:

php - 如何重定向到 symfony 的 web/app.php

python - 读取 xml 并尝试将其提取到 2 个不同的 xml 中

c# - 将 XML 反序列化为 C# 类

php - $criteria->在 yii 中选择

php - 最简单的 Javascript If 语句不起作用

php - 插入时MySQL语法错误

android - 使用 RadioButton/RadioGroup 行为创建 TextView(反之亦然)

html - 将 XML 转换为 HTML(相对于 xhtml)

c# - 在 Azure 逻辑应用中转换 XML,将 C# 程序集 (.DLL) 文件上传到 Azure 集成帐户并在 XSLT 中使用它(映射)

xml - XSLT 2.0 分组 <xsl :for-each-group>