代码之家  ›  专栏  ›  技术社区  ›  Benjamin Ortuzar

包装XML节点组

  •  4
  • Benjamin Ortuzar  · 技术社区  · 16 年前

    我使用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> 
    

    正如您在上面看到的,我需要将包装节点添加到任何尚未由“list”节点包装的“item”节点。

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

    更新:

    注1:任何一个或一组 <item> 节点需要用 <list> 节点(如果尚未包装)。

    注2:内容的顺序需要维护。

    注3: 如果有 <项目& GT; 前后节点 <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>
    

    谢谢,

    3 回复  |  直到 16 年前
        1
  •  4
  •   Dimitre Novatchev    16 年前

    这种转变 :

    <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增加了其他要求:

    如果有 item 元素之前 anothernode 之后,每一组 项目 元素必须用单独的 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: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>
    
        2
  •  3
  •   user357812    16 年前

    此样式表:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:template match="@*|node()" name="identity">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()[1]" />
            </xsl:copy>
            <xsl:apply-templates select="following-sibling::node()[1]" />
        </xsl:template>
        <xsl:template match="*[not(self::list)]
                              /item[not(preceding-sibling::*[1][self::item])]">
            <list>
                <xsl:call-template name="identity"/>
            </list>
            <xsl:apply-templates select="following-sibling::node()
                                          [not(self::item)][1]" />
        </xsl:template>
        <xsl:template match="*[not(self::list)]
                              /item[not(following-sibling::*[1][self::item])]">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()[1]" />
            </xsl:copy>
        </xsl:template>
    </xsl:stylesheet>
    

    输出:

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

    此外,此样式表:

    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
        <xsl:key name="kItemByFirstSibling"
                 match="item[preceding-sibling::*[1][self::item]]"
                 use="generate-id(preceding-sibling::item
                                   [not(preceding-sibling::*[1][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"/>
        <xsl:template match="*[not(self::list)]
                              /item[not(preceding-sibling::*[1][self::item])]"
                      priority="1">
            <list>
                <xsl:for-each select=".|key('kItemByFirstSibling',generate-id())">
                    <xsl:call-template name="identity"/>
                </xsl:for-each>
            </list>
        </xsl:template>
    </xsl:stylesheet>
    

    注释 :第一个样式表使用最细粒度的横向(它将在第一个之后包装任何节点 item )第二个样式表完全递归标识转换。

    编辑 :使用新输入寻址新的请求,两个样式表输出:

    <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>
    
        3
  •  0
  •   LarsH    16 年前

    您没有在原始问题中解决此问题,因此可能不需要。但是如果输入有多个序列 <item> 需要包装的元素,由其他同级元素相互分隔,例如:

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

    我相信,早先的答案会把 <项目& GT; 元素在一起,改变它们的顺序:

    <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>          
                <item label="c">some text</item>
                <item label="d">some text</item>
            </list> <!-- closing new wrapper node-->
            <anotherNode>some text</anotherNode>
        </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> 
    

    如果是后者,那么使用XSLT2.0可能最简单。 <xsl:for-each-group group-adjacent="name()" /> 建设。我不知道PHP5是否有可用的XSLT2.0,但是如果您可以使用它,请参见 this good article .