使用 xslt 重新排列 XML 个节点集

Rearranging XML nodesets using xslt

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

我收到以下错误:

Error on line 11 XPTY0020: Required item type of the context item for the child axis is node(); supplied value has item type xs:integer

在线版本在这里:http://xsltransform.net/gWmuiJw

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

当你这样做时:

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

会更有用。


警告:

identity transform 模板,当应用于 <students> 元素时,将模板应用于 all 个子节点 <students> - 不仅是 <student> 节点。

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

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

<xsl:template match="students">
    <xsl:copy>
        <xsl:apply-templates select="student"/>
    </xsl:copy>
</xsl:template>

从当前节点列表中排除 <student> 以外的任何节点。