Rag*_*ani 5 xml csv xslt transform xslt-1.0
我一直在尝试使用XSLT在最有效的时间从XML文档中获取CSV数据.以下是我的示例XML
<?xml version="1.0" encoding="ISO-8859-1"?>
<sObjects xmlns="urn:sobject.partner.soap.sforce.com">
<sObject>
<Name>Raagu</Name>
<BillingStreet>Hoskote</BillingStreet>
</sObject>
<sObject>
<Name>Rajath</Name>
<BillingStreet>BTM</BillingStreet>
<age>25</age>
</sObject>
<sObject>
<Name>Sarath</Name>
<BillingStreet>Murgesh</BillingStreet>
<location>Bangalore</location>
<age>#N/A</age>
</sObject>
<sObject>
<Name>Bharath</Name>
<BillingStreet>EGL</BillingStreet>
<location>Bangalore</location>
<shipping>Hoskote</Shipping>
</sObject>
<sObject>
<Id>12312321321</Id>
<Name>Guru</Name>
<location>Sirsi</location>
<date>12-12-12</date>
</sObject>
<sObject>
<Name>Appa</Name>
<BillingStreet>someStrrt</BillingStreet>
<accountNo>213213</accountNo>
</sObject>
<sObject>
<Name>Sarath</Name>
<BillingStreet>Murgesh</BillingStreet>
<location>Bangalore</location>
</sObject>
<sObject>
<Name>Sarath</Name>
<BillingStreet>Murgesh</BillingStreet>
<location>Bangalore</location>
</sObject>
<sObject>
<Name>Sarath</Name>
<BillingStreet>Murgesh</BillingStreet>
<location>Bangalore</location>
</sObject>
Run Code Online (Sandbox Code Playgroud)
我想要这种输出
<?xml version="1.0" encoding="utf-8"?><csv xmlns="http://www.approuter.com/schemas/RootNode"><data>Name,BillingStreet,age,location,Shipping,Id,date,accountNo
Raagu,Hoskote,,,,,,
Rajath,BTM,25,,,,,
Sarath,Murgesh,#N/A,Bangalore,,,,
Bharath,EGL,,Bangalore,Hoskote,,,
Guru,,,Sirsi,,12312321321,12-12-12,
Appa,someStrrt,,,,,,213213
Sarath,Murgesh,,Bangalore,,,,
Sarath,Murgesh,,Bangalore,,,,
Sarath,Murgesh,,Bangalore,,,,</data></csv>
Run Code Online (Sandbox Code Playgroud)
为了完成这项工作,我尝试了以下XSLT
<xsl:stylesheet version="1.0" xmlns:p0="urn:sobject.partner.soap.sforce.com" xmlns:csv="csv:csv" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="xml"/>
<xsl:strip-space elements="*" />
<xsl:variable name="delimiter" select="','"/>
<xsl:key name="field" match="p0:sObject/*" use="name()"/>
<!-- variable containing the first occurrence of each field -->
<xsl:variable name="allFields"
select="/*/*/*[generate-id()=generate-id(key('field', name())[1])]"/>
<xsl:template match="/">
<!-- Output the CSV header -->
<xsl:element name="csv" namespace="http://www.approuter.com/schemas/RootNode">
<xsl:element name="data" namespace="http://www.approuter.com/schemas/RootNode">
<xsl:for-each select="$allFields">
<xsl:value-of select="name()" />
<xsl:if test="position() < last()">
<xsl:value-of select="$delimiter" />
</xsl:if>
</xsl:for-each>
<xsl:text>
 </xsl:text>
<xsl:apply-templates select="/*/p0:sObject" />
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="p0:sObject">
<xsl:variable name="this" select="." />
<xsl:for-each select="$allFields">
<xsl:value-of select="$this/*[name() = name(current())]" />
<xsl:if test="position() < last()">
<xsl:value-of select="$delimiter" />
</xsl:if>
</xsl:for-each>
<xsl:if test="position() < last()">
<xsl:text>
 </xsl:text>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
从功能的角度来看,上面的XSLT工作得非常好.但我正在尝试处理大约10000条记录.i,在sObject元素上有10000个实例,每个sObject将包含大约15个字段.
如果我在XSLT上面运行它来处理这么多记录,它就会折腾.XSLT大约需要20分钟来处理和提供csv数据.我想在几秒钟内完成这项工作.也就是说,XSLT需要3-4秒才能处理10k条记录(sObject条目)以提供有效的CSV数据,如上所示.
这是我坚持增强XSLT并需要帮助修改此XSLT以更快地工作的地方.
我认为这是一个难题.我没有看到任何明显的东西.采用多步的构建是招:我创建pass1.xsl和pass2.xsl它创建输出树多快.
我制作了一个树大小为252097个节点(697768个字符)的测试文件.你的XSL需要21秒,而下面的两个xsls需要几秒钟.
<xsl:stylesheet version="2.0" xmlns:p0="urn:sobject.partner.soap.sforce.com"
xmlns:csv="csv:csv" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="xml" indent="no"/>
<xsl:strip-space elements="*" />
<xsl:variable name="delimiter" select="','"/>
<xsl:variable name="allFields">
<xsl:for-each select="distinct-values( /*/*/*/name() )">
<xsl:element name="{.}" />
</xsl:for-each>
</xsl:variable>
<xsl:template match="p0:sObjects">
<xsl:element name="{local-name(.)}" namespace="urn:sobject.partner.soap.sforce.com">
<xsl:element name="order" namespace="urn:sobject.partner.soap.sforce.com">
<xsl:for-each select="$allFields/*">
<xsl:value-of select="name()" />
<xsl:if test="position() < last()">
<xsl:value-of select="$delimiter" />
</xsl:if>
</xsl:for-each>
</xsl:element>
<xsl:apply-templates select="/*/p0:sObject" />
</xsl:element>
</xsl:template>
<xsl:template match="p0:sObject">
<xsl:variable name="this" select="." />
<xsl:element name="{local-name(.)}" namespace="urn:sobject.partner.soap.sforce.com">
<xsl:for-each select="$allFields/*">
<xsl:element name="{local-name(.)}" namespace="urn:sobject.partner.soap.sforce.com">
<xsl:value-of select="$this/*[name() = name(current())]" />
</xsl:element>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
<xsl:stylesheet version="2.0" xmlns:p0="urn:sobject.partner.soap.sforce.com"
xmlns:csv="csv:csv" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output encoding="utf-8" method="xml" indent="no"/>
<xsl:strip-space elements="*" />
<xsl:variable name="delimiter" select="','"/>
<xsl:template match="/">
<xsl:element name="csv" namespace="http://www.approuter.com/schemas/RootNode">
<xsl:element name="data" namespace="http://www.approuter.com/schemas/RootNode">
<xsl:apply-templates select="/p0:sObjects/*" />
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template match="p0:order">
<xsl:value-of select="." />
</xsl:template>
<!-- use Michael's suggestion about using first instead of last() -->
<xsl:template match="p0:sObject/*[ position() = 1 ]">
<xsl:text>
 </xsl:text>
<xsl:value-of select="." />
</xsl:template>
<xsl:template match="p0:sObject/*[ position() > 1 ]">
<xsl:value-of select="$delimiter" />
<xsl:value-of select="." />
</xsl:template>
</xsl:stylesheet>
Run Code Online (Sandbox Code Playgroud)
这是传递1的输出xml.这将创建一个看起来像这样的xml(下一阶段更容易解析.请参阅sObject下的新空元素?):
<sObjects xmlns="urn:sobject.partner.soap.sforce.com">
<order>Name,BillingStreet,age,location,shipping,Id,date,accountNo</order>
<sObject>
<Name>Raagu</Name>
<BillingStreet>Hoskote</BillingStreet>
<age/>
<location/>
<shipping/>
<Id/>
<date/>
<accountNo/>
</sObject>
</xObjects>
Run Code Online (Sandbox Code Playgroud)
<csv xmlns="http://www.approuter.com/schemas/RootNode"><data>Name,BillingStreet,age,location,shipping,Id,date,accountNo
Raagu,Hoskote,,,,,,
Rajath,BTM,25,,,,,
...
Run Code Online (Sandbox Code Playgroud)
我执行的命令:
saxonb-xslt -t bigxml.xml pass1.xsl > intermediate.xml
saxonb-xslt -t intermediate.xml pass2.xsl > res.xml
Run Code Online (Sandbox Code Playgroud)
一些建议:
如果您使用的是XSLT 2.0处理器,请不要设置version ="1.0".这使它以向后兼容模式运行,这涉及更多的运行时检查.
这种代码,在样式表中出现三次
<xsl:for-each select="$allFields">
<xsl:value-of select="name()" />
<xsl:if test="position() < last()">
<xsl:value-of select="$delimiter" />
</xsl:if>
</xsl:for-each>
Run Code Online (Sandbox Code Playgroud)
如果position()!= 1,则在项目之前插入分隔符,而不是在position()!= last()之后在项目之后插入分隔符,总是更好.那是因为last()是一项涉及前瞻的昂贵操作.在这种简单的情况下,整个事情可以被替换
<xsl:value-of select="$allFields/name()" separator="{$delimiter}"/>
Run Code Online (Sandbox Code Playgroud)如果您只打算使用一次键,那么定义键是没有意义的.查找唯一字段名称最好使用distinct-values(/*/*/*/name())
.
但是,我真的看不出为什么这需要20分钟,我不认为我已经确定了真正的问题是什么.