Insert a parent node for a group of nodes in XSLT

时间:2015-09-14 16:11:26

标签: xslt xslt-grouping

I need to transform using XSLT an XML input:

<CONTAINER>container1</CONTAINER>
<STOP>stop1</STOP>
<PO>po1</PO>
<PO>po2</PO>
<PO>po3</PO>
<CONTAINER>container2</CONTAINER>
<STOP>stop2</STOP>
<PO>po4</PO>
<PO>po5</PO>
<PO>po6</PO>
<STOP>stop3</STOP>
<PO>po7</PO>
<PO>po8</PO>

into

<CONTAINER>container1</CONTAINER>
<STOP>
<new_tag>Collapsed STOP</new_tag>
<PO>po1</PO>
<PO>po2</PO>
<PO>po3</PO>
</STOP>
<CONTAINER>container2</CONTAINER>
<STOP>
<new_tag>Collapsed STOP</new_tag>
<PO>po4</PO>
<PO>po5</PO>
<PO>po6</PO>
<STOP>stop3</STOP>
<PO>po7</PO>
<PO>po8</PO>
</STOP>

So basically I need to collapse all POs tag in a single STOP instead of having many STOPs, each one having a group of POs ad children.

Can someone help me on this? I'm very new to XSLT so I cannot fing a way to perform (if possible) this transformation.

2 个答案:

答案 0 :(得分:2)

假设

  1. 您的输入应该是格式良好的文档。我把它包裹在一个元素中就可以了。
  2. 您正在使用XSLT 2.您没有说明版本。
  3. 文档中的每个CONTAINER始终都会立即停止。
  4. 根元素的第一个子元素是CONTAINER。
  5. 这个XSLT 2转换......

    <xsl:stylesheet
      xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
      version="2.0">
    
    <xsl:output indent="yes" encoding="utf-8" omit-xml-declaration="yes" />
    <xsl:strip-space elements="*" />
    
    <xsl:template match="@*|node()">
      <xsl:copy>
        <xsl:apply-templates />
      </xsl:copy>
    </xsl:template>
    
    <xsl:template match="t">
      <xsl:copy>
        <xsl:for-each-group select="*" group-starting-with="CONTAINER">
          <CONTAINER><xsl:value-of select="current-group()[self::CONTAINER]/text()" /></CONTAINER>
          <STOP>
            <new_tag>Collapsed STOP</new_tag>
            <xsl:apply-templates select="current-group()
              [not(self::CONTAINER)]      (: Excluded because we have already dealt with CONTAINER.  :)
              [position() ge 2     ]      (: Exclude the first STOP, because already dealt with.     :)
              " />
          </STOP>
        </xsl:for-each-group>
      </xsl:copy>
    </xsl:template>
    
    </xsl:stylesheet>
    

    ...将转换此输入文档......

    <t>
        <CONTAINER>container1</CONTAINER>
        <STOP>stop1</STOP>
        <PO>po1</PO>
        <PO>po2</PO>
        <PO>po3</PO>
        <CONTAINER>container2</CONTAINER>
        <STOP>stop2</STOP>
        <PO>po4</PO>
        <PO>po5</PO>
        <PO>po6</PO>
        <STOP>stop3</STOP>
        <PO>po7</PO>
        <PO>po8</PO>
    </t>
    

    ...进入此输出...

    <t>
       <CONTAINER>container1</CONTAINER>
       <STOP>
          <new_tag>Collapsed STOP</new_tag>
          <PO>po1</PO>
          <PO>po2</PO>
          <PO>po3</PO>
       </STOP>
       <CONTAINER>container2</CONTAINER>
       <STOP>
          <new_tag>Collapsed STOP</new_tag>
          <PO>po4</PO>
          <PO>po5</PO>
          <PO>po6</PO>
          <STOP>stop3</STOP>
          <PO>po7</PO>
          <PO>po8</PO>
       </STOP>
    </t>
    

    学习笔记

    请参阅my answer here,了解有关xsl:for-each-group指令的更多信息。

答案 1 :(得分:0)

XSLT 1.0解决方案:

var videoLinks = [
    ....
];

var lastNumber = 0;
var randomNumber = function () {
    var getRandomNumber = Math.floor(Math.random() * 5);  
    if(getRandomNumber != lastNumber){
        var random = videoLinks[getRandomNumber];
        document.getElementById("videoWrapper").innerHTML = random[0];
        lastNumber = getRandomNumber;
    }else{
        randomNumber();
    }
};

randomNumber(); // To call the function on load

关键部分在这里:

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

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

    </xsl:template>

    <xsl:template match="/root/CONTAINER">
        <xsl:copy-of select="."/>
        <xsl:apply-templates />
    </xsl:template>

    <xsl:template match="/root/STOP">
        <STOP>
            <new_tag><xsl:value-of select="." /></new_tag>
            <xsl:variable name="stopnum">
                <xsl:number level="single" count="STOP" />
            </xsl:variable>
            <xsl:for-each select="following-sibling::PO[count(preceding-sibling::STOP) = $stopnum]">
                <xsl:copy-of select="." />                
            </xsl:for-each>
        </STOP>
    </xsl:template>

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

</xsl:transform>

我们设置一个变量,告诉我们已经处理的最后一个STOP节点的位置,然后选择我们的 <xsl:variable name="stopnum"> <xsl:number level="single" count="STOP" /> </xsl:variable> <xsl:for-each select="following-sibling::PO[count(preceding-sibling::STOP) = $stopnum]"> 中具有那么多for-each个STOP节点的所有PO节点。 / p>

请注意,我已将XML封装在preceding-sibling<root>节点中,以使其成为有效的XML。

输入:

</root>

输出:

<root>
<CONTAINER>container1</CONTAINER>
<STOP>stop1</STOP>
<PO>po1</PO>
<PO>po2</PO>
<PO>po3</PO>
<CONTAINER>container2</CONTAINER>
<STOP>stop2</STOP>
<PO>po4</PO>
<PO>po5</PO>
<PO>po6</PO>
<STOP>stop3</STOP>
<PO>po7</PO>
<PO>po8</PO>
</root>