XSLT 1.0:CSV 到 XML - 如何分而治之
Posted
技术标签:
【中文标题】XSLT 1.0:CSV 到 XML - 如何分而治之【英文标题】:XSLT 1.0: CSV to XML - How to divide and conquer 【发布时间】:2014-04-08 00:03:21 【问题描述】:在大量参考堆栈溢出答案后,我有了以下 XSL,它使用列标题作为每个相应单元格的节点名称将 CSV 转换为 XML。
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="xsl">
<xsl:output method="xml" encoding="utf-8" />
<xsl:variable name="newline" select="' '" />
<xsl:variable name="comma" select="','" />
<xsl:variable name="csv" select="." />
<xsl:variable name="fields" select="substring-before( concat( $csv, $newline ), $newline )" />
<xsl:template match="/">
<xsl:element name="EXCHANGE">
<xsl:element name="DDM">
<xsl:call-template name="write-row">
<xsl:with-param name="rows" select="substring-after( $csv, $newline)"/>
</xsl:call-template>
</xsl:element>
</xsl:element>
</xsl:template>
<xsl:template name="write-row">
<xsl:param name="rows"/>
<xsl:variable name="this-row" select="substring-before( concat( $rows, $newline ), $newline )" />
<xsl:variable name="remaining-rows" select="substring-after( $rows, $newline )" />
<xsl:if test="string-length($this-row) > 1">
<xsl:element name="DDMSRS">
<xsl:call-template name="write-item">
<xsl:with-param name="columns" select="$fields"/>
<xsl:with-param name="row" select="$this-row" />
</xsl:call-template>
</xsl:element>
</xsl:if>
<xsl:if test="string-length( $remaining-rows ) > 0">
<xsl:call-template name="write-row">
<xsl:with-param name="rows" select="$remaining-rows" />
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="write-item">
<xsl:param name="row"/>
<xsl:param name="columns"/>
<xsl:variable name="col" select="substring-before( concat( $columns, $comma ), $comma)" />
<xsl:variable name="remaining-items" select="substring-after( $row, $comma )" />
<xsl:variable name="remaining-columns" select="substring-after( $columns, $comma )" />
<xsl:if test="$col != ''">
<xsl:element name="$col">
<xsl:value-of select="substring-before( concat( $row, $comma ), $comma)" />
</xsl:element>
</xsl:if>
<xsl:if test="string-length( $remaining-items ) > 0">
<xsl:call-template name="write-item">
<xsl:with-param name="columns" select="$remaining-columns"/>
<xsl:with-param name="row" select="$remaining-items" />
</xsl:call-template>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
像这样在 csv 上运行 XSL(换行符是行分隔符):
<root><![CDATA[COL_HEAD1,COL_HEAD2,COL_HEAD3
123456789,Peter,My address
]]></root>
将返回以下 xml:
<?xml version="1.0" encoding="utf-8"?>
<EXCHANGE>
<DDM>
<DDMSRS>
<COL_HEAD1>123456789</COL_HEAD1>
<COL_HEAD2>Peter</COL_HEAD2>
<COL_HEAD3>My address</COL_HEAD3>
</DDMSRS>
</DDM>
</EXCHANGE>
我现在遇到的问题是,当我想处理 csv 中的很多行(1000 或更多)时,内存不足。
我在其他 *** 问题中看到了对分而治之的参考,但我不知道如何将我的字符串分成两半。
所以我的问题是:
-
在这种情况下如何执行分而治之?
还有其他方法可以提高此 XSL 的性能吗
使用 XSLT 1.0?
【问题讨论】:
您需要一个格式良好的 xml 文档才能运行 xslt,因此“拆分”听起来不是正确的做法。您是否仅限于使用 xslt 1.0?您可以尝试其他旨在解决 xslt 内存限制的技术(如 SAX)。您使用什么 XSLT 引擎?也许你可以试试更高效的。 我仅限于 xslt 1.0。当我说拆分时,我指的是将 csv 分成两半并分别处理每个位并重复除法过程。关键是 XSL 正在工作,我只需要使用 XSLT 1.0 本机功能提高性能。 我得到的具体错误是:(java.lang.OutOfMemoryError): Java heap space @Peter 如果使用某个工具很痛苦,请使用其他工具。不要使用 XSLT 1.0 来解决这个问题,而是使用 a) 适当的 CSV 解析器作为中间步骤(首选),b) 自定义 XSLT 扩展 written in Java that you can call from within the stylesheet 或 c) XSLT 扩展,如 EXSLT,它可以为您进行标记化.在 vanilla XSLT 1.0 中实现这样的东西是没有用的。 既然您使用的是 Java,为什么还要限制自己使用 XSLT 1.0?这在 XSLT 2.0 中会容易得多,它已经在 Java 世界中使用了大约十年。 【参考方案1】:在这种情况下,使用 XSLT 分而治之并非易事。有一些方法可以优化 XSLT 处理,您可能想尝试一个不同的处理器来做不同的事情,但是您的代码不容易改进,因为它实际上不进行任何 XML 处理。它在一个包含字符串的大单元素节点上运行。您实际上只使用 XPath 函数来解析字符串和 XSLT 变量来存储它们。消除 XSLT 开销会更有效。
您的选择包括:
-
增加内存(使用
-Xmx
选项)。
将文件分成更小的部分(需要格式良好的 XML)并在 XSLT 中分别处理每个部分。
但是 XSLT 不能帮助您选择 no。 2,因为要开始处理你的文件,它需要将它全部加载到内存中。它不能加载部分文本并将其拆分,因为每个片段都必须是格式正确的 XML。甚至 SAX 解析器也可能没有那么高效,因为您只有一个节点。您最好使用高效的字符串解析器,您可以在其中拆分 CSV,然后将每个片段包装在 XML 标记中。
【讨论】:
感谢楼主的回复。正如我在上面提到的,选项 2 似乎是唯一可行的解决方案。以上是关于XSLT 1.0:CSV 到 XML - 如何分而治之的主要内容,如果未能解决你的问题,请参考以下文章