使用 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>
以外的任何节点。
我想用 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>
以外的任何节点。