遵循中所述的方法:使用 XSLT 将 CSV 转换为分层 XML
现在,原始输入文件将包含空标记,如下所示:
<root>
GroupName,GroupValue,SubGroupName,SubGroupValue,ItemName,ItemValue
,A,1,C,1,G
1,,1,C,2,H
1,A,2,D,1,I
</root>
提供的原始 XSLT 1.0 是:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
extension-element-prefixes="exsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:key name="k1" match="row" use="cell[1]"/>
<xsl:key name="k2" match="row" use="concat(cell[1], '|', cell[3])"/>
<xsl:template match="/">
<!-- tokenize csv -->
<xsl:variable name="rows">
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="root"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="data">
<xsl:for-each select="exsl:node-set($rows)/row[position() > 1]">
<row>
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="."/>
<xsl:with-param name="delimiter" select="','"/>
<xsl:with-param name="name" select="'cell'"/>
</xsl:call-template>
</row>
</xsl:for-each>
</xsl:variable>
<!-- output -->
<document>
<xsl:for-each select="exsl:node-set($data)/row[count(. | key('k1', cell[1])[1]) = 1]">
<group>
<name>
<xsl:value-of select="cell[1]"/>
</name>
<value>
<xsl:value-of select="cell[2]"/>
</value>
<xsl:for-each select="key('k1', cell[1])[count(. | key('k2', concat(cell[1], '|', cell[3]))[1]) = 1]">
<subgroup>
<name>
<xsl:value-of select="cell[3]"/>
</name>
<value>
<xsl:value-of select="cell[4]"/>
</value>
<items>
<xsl:for-each select="key('k2', concat(cell[1], '|', cell[3]))">
<item>
<name>
<xsl:value-of select="cell[5]"/>
</name>
<value>
<xsl:value-of select="cell[6]"/>
</value>
</item>
</xsl:for-each>
</items>
</subgroup>
</xsl:for-each>
</group>
</xsl:for-each>
</document>
</xsl:template>
<xsl:template name="tokenize">
<xsl:param name="text"/>
<xsl:param name="delimiter" select="' '"/>
<xsl:param name="name" select="'row'"/>
<xsl:variable name="token" select="substring-before(concat($text, $delimiter), $delimiter)" />
<xsl:if test="$token">
<xsl:element name="{$name}">
<xsl:value-of select="$token"/>
</xsl:element>
</xsl:if>
<xsl:if test="contains($text, $delimiter)">
<!-- recursive call -->
<xsl:call-template name="tokenize">
<xsl:with-param name="text" select="substring-after($text, $delimiter)"/>
<xsl:with-param name="delimiter" select="$delimiter"/>
<xsl:with-param name="name" select="$name"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
如何调整 xslt 以便它不会在这种情况下跳过空标记并生成以下 xml 输出?
<?xml version="1.0" encoding="utf-8"?>
<Document>
<data>
<GroupName></GroupName>
<GroupValue>A</GroupValue>
...
</data>
<data>
<GroupName>1</GroupName>
<GroupValue></GroupValue>
...
</data>
<data>
<GroupName>1</GroupName>
<GroupValue>A</GroupValue>
...
</data>
</Document>
试试这个:
您也可以在 http://xsltransform.net/nb9MWt1/2
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
exclude-result-prefixes="exsl"
version="1.0">
<xsl:output indent="yes"/>
<xsl:variable name="elements">
<element>GroupName</element>
<element>GroupValue</element>
<element>SubGroupName</element>
<element>SubGroupValue</element>
<element>ItemName</element>
<element>ItemValue</element>
</xsl:variable>
<xsl:template match="root">
<Document>
<xsl:call-template name="row">
<xsl:with-param name="data" select="."/>
</xsl:call-template>
</Document>
</xsl:template>
<xsl:template name="row">
<xsl:param name="data"/>
<xsl:choose>
<xsl:when test="contains($data, '
')">
<xsl:if test="normalize-space(substring-before($data, '
')) != ''">
<data>
<xsl:call-template name="cell">
<xsl:with-param name="celldata" select="substring-before($data, '
')"/>
<xsl:with-param name="position" select="1"/>
</xsl:call-template>
</data>
</xsl:if>
<xsl:if test="normalize-space(substring-after($data, '
')) != ''">
<xsl:call-template name="row">
<xsl:with-param name="data" select="substring-after($data, '
')"/>
<xsl:with-param name="position" select="1"/>
</xsl:call-template>
</xsl:if>
</xsl:when>
<xsl:otherwise>
<xsl:if test="normalize-space($data) != ''">
<data>
<xsl:call-template name="cell">
<xsl:with-param name="celldata" select="$data"/>
<xsl:with-param name="position" select="1"/>
</xsl:call-template>
</data>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="cell">
<xsl:param name="celldata"/>
<xsl:param name="position"/>
<xsl:choose>
<xsl:when test="contains($celldata, ',')">
<xsl:element name="{exsl:node-set($elements)//element[position() = $position]}">
<xsl:value-of select="substring-before($celldata, ',')"/>
</xsl:element>
<xsl:call-template name="cell">
<xsl:with-param name="celldata" select="substring-after($celldata, ',')"/>
<xsl:with-param name="position" select="number($position) + 1"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="{$elements//element[position() = $position]}">
<xsl:value-of select="$celldata"/>
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
抱歉使用它作为注释,但不确定如何通过注释格式化代码:谢谢鲁佩什,但如果1(只有几个字段被选择在输出文件中,2(字段名称可能需要更改为输出中的其他名称?
对于 1(,将 xsl 更改为以下内容以仅输出 3 个选定字段
<xsl:variable name="elements">
<element>SubGroupName</element>
<element>ItemName</element>
<element>ItemValue</element>
</xsl:variable>
不行?
对于 2(,假设 GroupValue 在输出中重命名为"Field1",在输出中将"ItemName"重命名为"Field2",这是唯一需要的 2 个字段......
<?xml version="1.0" encoding="utf-8"?>
<Document>
<data>
<Field1>A</Field1>
<Field2>1</Field2>
</data>
<data>
<Field1></Field1>
<Field2>2</Field2>
</data>
<data>
<Field1>A</Field1>
<Field2>1</Field2>
</data>
</Document>
最后,输入字段可能包含空格,例如
<root>
Group Name,Group Value,Sub Group Name,Sub Group Value,Item Name,Item Value
,A,1,C,1,G
1,,1,C,2,H
1,A,2,D,1,I
</root>