使用XSLT分两步将CSV输入转换为XML。在第一次CSV-XML转换csv2xml
之后,我想将属于同一行的所有元素分组到一个元素中,其中从第一行(标题)读取属性,即。
初始(CSV)输入
<signature>
stroke,x,y,t
1,0.585,0.226,1460579160811
1,0.653,0.231,1460579160812
1,1.251,1.125,1460579160813
1,2.224,1.672,1460579160814
1,2.716,1.761,1460579160815
1,3.675,1.877,1460579160816
1,4.008,1.984,1460579160817
1,4.888,2.81,1460579160818
</signature>
输出1(=输入2)
<column row="1" col="1">stroke</column>
<column row="1" col="2">x</column>
<column row="1" col="3">y</column>
<column row="1" col="4">t</column>
<column row="2" col="1">1</column>
<column row="2" col="2">0.585</column>
<column row="2" col="3">0.226</column>
<column row="2" col="4">1460579160811</column>
<column row="3" col="1">1</column>
<column row="3" col="2">0.653</column>
<column row="3" col="3">0.231</column>
<column row="3" col="4">1460579160812</column>
<column row="4" col="1">1</column>
<column row="4" col="2">1.251</column>
<column row="4" col="3">1.125</column>
<column row="4" col="4">1460579160813</column>
期望的最终输出
<?xml version="1.0"?>
<signature>
<row stroke="1" x="0.585" y="0.226" t="1460579160811"/>
<row stroke="1" x="0.653" y="0.231" t="1460579160812"/>
<row stroke="1" x="1.251" y="1.125" t="1460579160813"/>
<row stroke="1" x="2.224" y="1.672" t="1460579160814"/>
我使用以下XSLT模板实现:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xsl:stylesheet
xmlns:exsl="http://exslt.org/common"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
extension-element-prefixes="exsl"
version="1.0">
<xsl:strip-space elements="root"/>
<xsl:output method="xml" omit-xml-declaration="no"/>
<!-- Version -->
<xsl:variable name="xslver" select='1' />
<xsl:template name="csv2xml">
<xsl:param name="input" select="''"/>
<xsl:param name="column" select="1"/>
<xsl:param name="row" select="1"/>
<xsl:choose>
<xsl:when test="contains(substring-before($input,','),' ')">
<xsl:element name="column">
<xsl:attribute name="row">
<xsl:value-of select="$row"/>
</xsl:attribute>
<xsl:attribute name="col">
<xsl:value-of select="$column"/>
</xsl:attribute>
<xsl:value-of select="substring-before($input,' ')"/>
</xsl:element>
<xsl:call-template name="csv2xml">
<xsl:with-param name="input" select="substring-after($input,' ')" />
<xsl:with-param name="row" select="$row+1"/>
<xsl:with-param name="column" select="1"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($input,',')">
<xsl:element name="column">
<xsl:attribute name="row">
<xsl:value-of select="$row"/>
</xsl:attribute>
<xsl:attribute name="col">
<xsl:value-of select="$column"/>
</xsl:attribute>
<xsl:value-of select="substring-before($input,',')"/>
</xsl:element>
<xsl:call-template name="csv2xml">
<xsl:with-param name="input" select="substring-after($input,',')" />
<xsl:with-param name="row" select="$row"/>
<xsl:with-param name="column" select="$column + 1" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:element name="column">
<xsl:attribute name="row">
<xsl:value-of select="$row"/>
</xsl:attribute>
<xsl:attribute name="col">
<xsl:value-of select="$column"/>
</xsl:attribute>
<xsl:value-of select="$input" />
</xsl:element>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="remap">
<xsl:param name="input"/>
<xsl:variable name="output">
<xsl:call-template name="csv2xml">
<xsl:with-param name="input" select="$input"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="csv" select="exsl:node-set($output)"/>
<xsl:variable name="headers" select="$csv/column[@row=1]"/>
<xsl:for-each select="$csv/column[not(@row = following-sibling::column/@row)]">
<xsl:variable name="pos" select="position()"/>
<xsl:variable name="rows" select="$csv/column[@row=$pos]"/>
<xsl:element name="row">
<xsl:for-each select="$headers">
<xsl:variable name="attr" select="text()"/>
<xsl:variable name="pos2" select="position()"/>
<xsl:attribute name="{$attr}"><xsl:value-of select="$rows[@col=$pos2]/text()"/></xsl:attribute>
</xsl:for-each>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:param name="root" value="'root'"/>
<xsl:template match="/">
<xsl:variable name="trimmed">
<xsl:call-template name="trim">
<xsl:with-param name="input" select="*[local-name() = $root]"/>
</xsl:call-template>
</xsl:variable>
<xsl:element name="{$root}">
<xsl:call-template name="remap">
<xsl:with-param name="input" select="$trimmed"/>
</xsl:call-template>
</xsl:element>
</xsl:template>
<xsl:template name="trim">
<xsl:param name="input" select="''"/>
<xsl:variable name="out-rtrim">
<xsl:call-template name="rtrim">
<xsl:with-param name="input" select="$input"/>
</xsl:call-template>
</xsl:variable>
<xsl:variable name="out-ltrim">
<xsl:call-template name="ltrim">
<xsl:with-param name="input" select="$out-rtrim"/>
</xsl:call-template>
</xsl:variable>
<xsl:value-of select="$out-ltrim"/>
</xsl:template>
<xsl:template name="rtrim">
<xsl:param name="input">"</xsl:param>
<xsl:choose>
<xsl:when test="string-length($input) > 0">
<xsl:variable name="first" select="substring($input,1,1)"/>
<xsl:variable name="remainder" select="substring($input,2)"/>
<xsl:choose>
<xsl:when test="string-length(normalize-space($first)) > 0">
<xsl:value-of select="$input"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="rtrim">
<xsl:with-param name="input" select="$remainder"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
<xsl:template name="ltrim">
<xsl:param name="input">"</xsl:param>
<xsl:choose>
<xsl:when test="string-length($input) > 0">
<xsl:variable name="head" select="substring($input,1,string-length($input)-1)"/>
<xsl:variable name="tail" select="substring($input,string-length($input),1)"/>
<xsl:choose>
<xsl:when test="string-length(normalize-space($tail)) > 0">
<xsl:value-of select="$input"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="ltrim">
<xsl:with-param name="input" select="$head"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
按预期工作,但速度不快:输入500行的xsltproc大约需要1000ms,其中98%用于上面的转换,即。第2步。
有没有办法改善这个?也许通过展平两个xsl:for-each
?
答案 0 :(得分:1)
- 根据您的澄清进行编辑 -
您的单元格已经分组为输入中的行。为什么你不做:
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns:str="http://exslt.org/strings"
extension-element-prefixes="exsl str">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/signature">
<!-- first pass: convert CSV to XML -->
<xsl:variable name="table-rtf">
<xsl:for-each select="str:tokenize(., ' ')">
<row>
<xsl:for-each select="str:tokenize(., ',')">
<col>
<xsl:value-of select="."/>
</col>
</xsl:for-each>
</row>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="table" select="exsl:node-set($table-rtf)"/>
<!-- output -->
<xsl:variable name="header" select="$table/row[1]" />
<xsl:copy>
<xsl:for-each select="$table/row[position() > 1]">
<row>
<xsl:for-each select="col">
<xsl:variable name="i" select="position()" />
<xsl:attribute name="{$header/col[$i]}">
<xsl:value-of select="."/>
</xsl:attribute>
</xsl:for-each>
</row>
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
这假设您的处理器支持str:tokenize()
- 正如libxslt那样。