似乎比运行eXSLT的速度慢于XSLT2中的对应物。 (7分钟vs 18小时)
下面我解释我的问题,在eXSLT和XSLT2中写下同一转换的两个实现。
当然,引擎是不同的,因为XSLT2我使用SaxonHE,而对于eXSLT我使用python和lxml。
最后我请求帮助提高eXSLT部分的速度,因为我更喜欢使用python而不是Java。
我必须将大型(~200k第1层元素)XML转换为csv。
我有2个实现:
由于编写CSV时,即使元素没有值,也必须打印分隔符,我采用了这种方法:
我创建了2个功能:
myf:printElement
接收一个Element和一个数字,表示如果元素为空则必须写入的分隔符数。
myf:printAttr
接收属性,并将其打印到分隔符。
如果我还将分隔符定义为:
<xsl:param name="delim" select="','"/>
函数在每个文件中声明如下:
XSLT2
<!-- Shortcut function to print an attribute plus a delimiter -->
<xsl:function name="myf:printAttr" as="xs:string">
<xsl:param name="pAttr" as="attribute()*"/>
<xsl:value-of select="concat($pAttr,$delim)"/>
</xsl:function>
<!-- This function will call the apply templates if the given elements exist. Else, it will return as many delimiters as the number given as second parameter -->
<xsl:function name="myf:printElement" as="item()*">
<xsl:param name="pElement" as="element()*"/>
<xsl:param name="pCount" as="xs:integer"/>
<xsl:choose>
<xsl:when test="$pElement">
<xsl:apply-templates select="$pElement"/>
</xsl:when>
<xsl:otherwise>
<!-- explicit void separator or will add an space -->
<xsl:value-of select="for $i in 1 to $pCount return $delim" separator=""/>
</xsl:otherwise>
</xsl:choose>
</xsl:function>
EXSLT
<!-- Shortcut function to print an attribute plus a delimiter -->
<func:function name="myf:printAttr">
<xsl:param name="pAttr"/>
<func:result select="concat($pAttr,$delim)"/>
</func:function>
<!-- This function will call the apply templates if the given elements exist. Else, it will return as many delimiters as the number given as second parameter -->
<func:function name="myf:printElement" as="item()*">
<xsl:param name="pElement" as="element()*"/>
<xsl:param name="pCount" as="xs:integer"/>
<xsl:choose>
<xsl:when test="$pElement">
<func:result>
<xsl:apply-templates select="$pElement"/>
</func:result>
</xsl:when>
<xsl:otherwise>
<!-- explicit void separator or will add an space -->
<func:result select="str:padding($pCount,$delim)"/>
</xsl:otherwise>
</xsl:choose>
</func:function>
其余文件是相同的。
所以,假设我有这样的XML:
<root>
<Tier1 attr1="A" attr2="B"/>
<Tier1 attr1="C" attr2="D">
<Child2 type="1" val="ABC"/>
<Child2 type="3" val="123"/>
</Tier1>
<Tier1 attr1="E" attr2="F">
<Child2 type="2" val="pancakes"/>
<Child2 type="1" val="42"/>
<Child3 a="H">
<Child4 Month="JUN"/>
</Child3>
</Tier1>
</root>
使用:
<xsl:param name="break" select="'
'"/>
<xsl:template match="/">
<xsl:apply-templates select="root/Tier1"/>`
</xsl:template>
<xsl:template match="Tier1">
<xsl:value-of select="myf:printAttr(@attr1)"/>
<xsl:value-of select="myf:printAttr(@attr2)"/>
<xsl:value-of select="myf:printAttr(Child2[@type='1']/@val)"/>
<xsl:value-of select="myf:printAttr(Child2[@type='2']/@val)"/>
<xsl:value-of select="myf:printAttr(Child2[@type='3']/@val)"/>
<xsl:apply-templates/>
<!-- line break after each Tier1 -->
<xsl:if test="following-sibling::*">
<xsl:value-of select="$break"/>
</xsl:if>
</xsl:template>
<xsl:template match="Child3">
<xsl:value-of select="myf:printAttr(@a)"/>
<xsl:value-of select="ama:printElement(Child4,3)"/>
</xsl:template>
<xsl:template match="Child4">
<xsl:value-of select="myf:printAttr(@Day)"/>
<xsl:value-of select="myf:printAttr(@Month)"/>
<!-- We dont want comma after last element-->
<xsl:value-of select=@Average/>
</xsl:template>
我会得到所需的csv输出:
T1_attr1, T1_attr2, C2_t1, C2_t2, C2_t3, C3_a, C4_Mont, C4_Day, C4_Average
A,B,,,,,,,
C,D,ABC,,123,,,,
E,F,42,pancakes,,H,JUN,3,1200
关于上述内容的一些注意事项:
可以在Tier1下重复Child2,但只有一组给定的值类型,而不是重复。
此外,元素中没有文字,这使得这两种功能的方法涵盖了我可能遇到的所有可能情况。虽然printAttr也可能适用于文本节点。
我添加了列名,以便于阅读。在代码中我在start时添加它,一个用eXSLT设置的内部节点,一个带XSLT2的简单字符串数组。
正如我在开始时所说的,我必须将变换运行到一个巨大的文件,其中包含超过20万个Tier1元素。
两个转换脚本/程序都这样做:
我知道我在谈论变换引擎的不同实现,但是由于这个原因,这种差异太明显了。 测试相同引擎的唯一方法是使用Saxon-PE或Saxon-EE下的eXSLT,因为它在Saxon-HE中不可用。 当然,python中没有XSLT2实现。
我想知道为什么python版本需要太长时间。这是使用eXSLT固有的吗?或有没有办法改善这个?
当然这是一个示例XML,真正的XML有很多元素,而且它实际上更复杂。
这是一个较大项目的一部分,我只想依赖于JVM,但是,差异是如此之大,以至于现在,Python不是一个选项。
谢谢!
答案 0 :(得分:2)
对我来说,看起来好像你正在大量过度设计这个问题。
以下简单的XSLT 1.0转换
<xsl:transform version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text" encoding="utf-8" />
<xsl:template match="/root">
<xsl:text>T1_attr1,T1_attr2,C2_t1,C2_t2,C2_t3,C3_a,C4_Month,C4_Day,C4_Average</xsl:text>
<xsl:apply-templates select="Tier1" />
</xsl:template>
<xsl:template match="Tier1">
<xsl:text>
</xsl:text>
<xsl:value-of select="@attr1" /> <xsl:text>,</xsl:text>
<xsl:value-of select="@attr2" /> <xsl:text>,</xsl:text>
<xsl:value-of select="Child2[@type = '1']/@val" /> <xsl:text>,</xsl:text>
<xsl:value-of select="Child2[@type = '2']/@val" /> <xsl:text>,</xsl:text>
<xsl:value-of select="Child2[@type = '3']/@val" /> <xsl:text>,</xsl:text>
<xsl:value-of select="Child3/@a" /> <xsl:text>,</xsl:text>
<xsl:value-of select="Child3/Child4/@Month" /> <xsl:text>,</xsl:text>
<xsl:value-of select="Child3/Child4/@Day" /> <xsl:text>,</xsl:text>
<xsl:value-of select="Child3/Child4/@Average" />
</xsl:template>
</xsl:transform>
应用于
<root>
<Tier1 attr1="A" attr2="B">
</Tier1>
<Tier1 attr1="C" attr2="D">
<Child2 type="1" val="ABC" />
<Child2 type="3" val="123" />
</Tier1>
<Tier1 attr1="E" attr2="F">
<Child2 type="2" val="pancakes" />
<Child2 type="1" val="42" />
<Child3 a="H">
<Child4 Month="JUN" Day="3" Average="1200" />
</Child3>
</Tier1>
</root>
产生
T1_attr1,T1_attr2,C2_t1,C2_t2,C2_t3,C3_a,C4_Month,C4_Day,C4_Average A,B,,,,,,, C,D,ABC,,123,,,, E,F,42,pancakes,,H,JUN,3,1200