使用xslt重新排列XML节点集

时间:2015-03-27 13:55:56

标签: xslt xslt-2.0

我想用xslt重新排列xml。

源xml如下:

<?xml version="1.0" encoding="UTF-8" ?>
<studentDetails>
<departments>
    <name>IT</name>
    <name>CSE</name>
    <name>EEE</name>
</departments>
<students>                   // Want to arrange students and marks alternatively
    <student>
        <name>aaa</name>
        <age>20</age>
    </student>
    <student>
        <name>bbb</name>
        <age>25</age>
    </student>
    <student>
        <name>ccc</name>
        <age>27</age>
    </student>
    <student>
        <name>ddd</name>
        <age>23</age>
    </student>
    <marks>
        <maths>60</maths>
        <english>65</english>
    </marks>
    <marks>
        <maths>70</maths>
        <english>75</english>
    </marks>
    <marks>
        <maths>80</maths>
        <english>85</english>
    </marks>
    <marks>
        <maths>90</maths>
        <english>95</english>
    </marks>
</students>
</studentDetails>

我想以这种方式输出:

<?xml version="1.0" encoding="UTF-8" ?>
<studentDetails>
<departments>
    <name>IT</name>
    <name>CSE</name>
    <name>EEE</name>
</departments>
<students>
    <student>
        <name>aaa</name>
        <age>20</age>
    </student>
    <marks>
        <maths>60</maths>
        <english>65</english>
    </marks>        
    <student>
        <name>bbb</name>
        <age>25</age>
    </student>
    <marks>
        <maths>70</maths>
        <english>75</english>
    </marks>    
    <student>
        <name>ccc</name>
        <age>27</age>
    </student>
    <marks>
        <maths>80</maths>
        <english>85</english>
    </marks>
    <student>
        <name>ddd</name>
        <age>23</age>
    </student>
    <marks>
        <maths>90</maths>
        <english>95</english>
    </marks>
</students>
</studentDetails>

1)。 <student>代码的数量等于<marks>的代号 2)。 <student><marks>的序列是线性的,<student>的第一次出现后应首先出现<marks>

我尝试了以下xslt:

<?xml version="1.0" encoding="UTF-8" ?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:variable name="countVar" select="count(studentDetails/students/student)"></xsl:variable>
  <xsl:variable name="i" select="0"></xsl:variable>
  <xsl:template match="/">
  <xsl:for-each select="1 to $countVar">
  <xsl:variable name="counter" select="$i + 1"></xsl:variable>
  <xsl:choose>
  <xsl:when test="(position() mod 2) = 0">
    <xsl:copy-of select="studentDetails/students/marks[$counter]"></xsl:copy-of>
  </xsl:when>
  <xsl:otherwise>
 <xsl:copy-of select="studentDetails/students/student[$counter]"></xsl:copy-of>
  </xsl:otherwise>
</xsl:choose>
  </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>

我收到以下错误:

  

第11行出错     XPTY0020:子轴的上下文项的必需项类型是node();供应     值具有项类型xs:integer

此处的在线版本:http://xsltransform.net/gWmuiJw

请告诉我哪里出错了。提前致谢。! :)

1 个答案:

答案 0 :(得分:2)

当你这样做时:

<xsl:for-each select="1 to $countVar">

您正在将上下文从XML输入切换到生成的整数序列。在这种情况下,您尝试使用的XPath "studentDetails/students/marks"毫无意义。

为什么不尝试更简单的事情:

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<xsl:template match="student">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
    <xsl:variable name="i" select="position()" />
    <xsl:copy-of select="../marks[$i]"/>
</xsl:template>

<xsl:template match="marks"/>

</xsl:stylesheet>

注意
恕我直言,一种在单个元素下包装学生的所有细节的格式,例如:

<students>
    <student>
        <name>aaa</name>
        <age>20</age>
        <marks>
            <maths>60</maths>
            <english>65</english>
        </marks>
    </student>
    ...
<students>

会更有用。


警告:

身份转换模板应用于<students>元素时,会将模板应用于<students>所有子节点 - 不仅仅是{ {1}}个节点。

在您的示例中,<student>元素在其子元素中也有一个注释节点(实际上,它是一个文本节点,但对于此解释的目的没有任何区别)。每个子节点的位置在<students>指令选择的节点集的上下文中计算(这称为current node list)。因此,示例中第一个xsl:apply templates节点的位置实际上是#2 - 因为注释节点占用位置#1。

这在实际制作中不应该是一个问题,如果在您的真实XML输入中,除了<student>之外,<students>不会是任何子节点。如果您无法确定,请在样式表中再添加一个模板:

<student>

从当前节点列表中排除<xsl:template match="students"> <xsl:copy> <xsl:apply-templates select="student"/> </xsl:copy> </xsl:template> 以外的任何节点。