我正在努力将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">
查找下一个测试,则必须在</xsl:if>
之前输入结束</case>
,因此我需要在该语句中迭代以下同级行(测试步骤)。我该如何做到这一点?
这是我对 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() > 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>