Excel的XML到XML – xslt迭代内循环

我努力将一个Excel的XML文件转换为另一种XML格式。

以下是源代码的简化摘录:

<Workbook> <Worksheet> <Table> <Row> <Cell> <Data>Test 1</Data> </Cell> <Cell> <Data>Preconditions for test 1</Data> </Cell> <Cell> <Data>The setup for test 1</Data> </Cell> <Cell /> </Row> <Row> <Cell /> <Cell> <Data>Step 1</Data> </Cell> <Cell> <Data>Todo in step 1</Data> </Cell> <Cell> <Data>Expected result</Data> <!--omitted if empty--> </Cell> </Row> . . <Row> <Cell /> <Cell> <Data>Step n</Data> </Cell> <Cell> <Data>Todo in step n</Data> </Cell> <Cell> <Data>Expected result</Data> <!--omitted if empty--> </Cell> </Row> . . ----------------- . . <Row> <Cell> <Data>Test m</Data> </Cell> <Cell> <Data>Preconditions for test m</Data> </Cell> <Cell> <Data>The setup for test m</Data> </Cell> <Cell /> </Row> <Row> <Cell /> <Cell> <Data>Step 1</Data> </Cell> <Cell> <Data>Todo in step 1</Data> </Cell> <Cell /> <Cell> <Data>Expected result</Data> <!--omitted if empty--> </Cell> </Row> . . <Row> <Cell /> <Cell> <Data>Step k</Data> </Cell> <Cell> <Data>Todo in step k</Data> </Cell> <Cell> <Data>Expected result</Data> <!--omitted if empty--> </Cell> </Row> </Table> </Worksheet> </Workbook> 

列2(单元格[2])可以被视为第3-4列的标题

我希望的输出应该是以下格式:

 <case> <title>Test 1</title> <precond>The setup for test 1</precond> <step> <index>1</index> <content>Todo in step 1</content> <expected>Expected result</expected> <!--omitted if empty--> </step> . . <step> <index>n</index> <content>Todo in step n</content> <expected>Expected result</expected> <!--omitted if empty--> </step> </case> . . ....... . . <case> <title>Test m</title> <precond>The setup for test m</precond> <step> <index>1</index> <content>Todo in step 1</content> <expected>Expected result</expected> <!--omitted if empty--> </step> . . <step> <index>n</index> <content>Todo in step n</content> <expected>Expected result</expected> <!--omitted if empty--> </step> </case> 

我的问题是定义xslt,以便<case>标签包含所有行,直到在Cell [1]中有一个数据条目的下一行。 如果我使用<xsl:if test="Cell[1]/Data">来查找下一个testing ,则必须在</xsl:if>之前inputclosures</case> </xsl:if> ,因此我需要迭代以下兄弟行(testing步骤)。 我如何做到这一点?

这是我在xslt上的微弱尝试:

 <?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="utf-8" indent="yes" /> <xsl:template match="Workbook/Worksheet/Table"> <xsl:for-each select="Row"> <xsl:if test="Cell[1]/Data"> <case> <title> <xsl:value-of select="Cell[1]/Data"/> </title> <xsl:if test="Cell[3]/Data"> <precond> <xsl:value-of select="Cell[3]/Data"/> </precond> </xsl:if> <!-- Here I need to iterate "while not" following-sibling::Cell[1]/Data and extract the data from cells 2-4.--> </case> </xsl:if> </xsl:for-each> </xsl:template> 

如果您遇到XSLT 1.0,索引是一种可能的简单方法:

 <xsl:key name="steps" match="Row[not(Cell[1]/Data)]" use="generate-id(preceding-sibling::Row[Cell[1]/Data][1])"/> <xsl:template match="/"> <result> <xsl:for-each select="//Row[Cell[1]/Data]"> <case> <title><xsl:value-of select="Cell[1]/Data"/></title> <precond><xsl:value-of select="Cell[3]/Data"/></precond> <xsl:for-each select="key('steps', generate-id(.))"> <step> <index><xsl:value-of select="Cell[2]/Data" /></index> <content><xsl:value-of select="Cell[3]/Data" /></content> <expected><xsl:value-of select="Cell[4]/Data" /></expected> </step> </xsl:for-each> </case> </xsl:for-each> </result> </xsl:template> 

如果您能够使用XSLT 2.0,则可以使用以下XSLT作为示例:

 <?xml version="1.0" encoding="UTF-8"?> <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"/> <!-- Identity template to 'loop' through all input XML (nodes and attributes) --> <xsl:template match="@*|node()"> <xsl:apply-templates select="@*|node()" /> </xsl:template> <!-- create new root element --> <xsl:template match="Workbook"> <data> <xsl:apply-templates select="@*|node()" /> </data> </xsl:template> <!-- Match on Table element, to do some grouping --> <xsl:template match="Table"> <!-- Group all rows together, each group starts with a Row where Cell[1] is not empty --> <xsl:for-each-group select="Row" group-starting-with="Row[Cell[1] != '']"> <case> <title><xsl:value-of select="Cell[1]/Data" /></title> <precond><xsl:value-of select="Cell[3]/Data" /></precond> <!-- Loop through the group, but forget the first occurence, since it is the header, use before this --> <xsl:for-each select="current-group()[position() &gt; 1]"> <step> <index><xsl:value-of select="Cell[2]/Data" /></index> <content><xsl:value-of select="Cell[3]/Data" /></content> <expected><xsl:value-of select="Cell[4]/Data" /></expected> </step> </xsl:for-each> </case> </xsl:for-each-group> </xsl:template> </xsl:stylesheet> 

以下XSLT将在XSLT 1.0中甚至在XSLT 2.0中起作用:

 <?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/> <!-- Identity template to 'loop' through all input XML (nodes and attributes) --> <xsl:template match="@*|node()"> <xsl:apply-templates select="@*|node()" /> </xsl:template> <!-- create new root element --> <xsl:template match="Workbook"> <data> <xsl:apply-templates select="descendant::Row[Cell[1] != '']" /> </data> </xsl:template> <!-- Match Rows where first Cell is empty and create the <case> element with header information --> <xsl:template match="Row[Cell[1] != '']"> <case> <title><xsl:value-of select="Cell[1]/Data" /></title> <precond><xsl:value-of select="Cell[3]/Data" /></precond> <xsl:choose> <xsl:when test="count(following-sibling::Row[Cell[1] != '']) = 0"> <!-- When there are no next Row elements with empty first Cells, we can just process the remaining Rows --> <xsl:apply-templates select="following-sibling::Row[Cell[1] = '']" /> </xsl:when> <xsl:otherwise> <!-- There are still Rows with empty first Cells, so we only process the Rows in between --> <xsl:apply-templates select="following-sibling::Row[Cell[1] != ''][1]/preceding-sibling::Row[Cell[1] = '']" /> </xsl:otherwise> </xsl:choose> </case> </xsl:template> <!-- Process Rows with empty first Cells, which will be the step information --> <xsl:template match="Row[Cell[1] = '']"> <step> <index><xsl:value-of select="Cell[2]/Data" /></index> <content><xsl:value-of select="Cell[3]/Data" /></content> <expected><xsl:value-of select="Cell[4]/Data" /></expected> </step> </xsl:template> </xsl:stylesheet>