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="'&#10;'" />
    <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 中会容易得多,它已经在 J​​ava 世界中使用了大约十年。 【参考方案1】:

在这种情况下,使用 XSLT 分而治之并非易事。有一些方法可以优化 XSLT 处理,您可能想尝试一个不同的处理器来做不同的事情,但是您的代码不容易改进,因为它实际上不进行任何 XML 处理。它在一个包含字符串的大单元素节点上运行。您实际上只使用 XPath 函数来解析字符串和 XSLT 变量来存储它们。消除 XSLT 开销会更有效。

您的选择包括:

    增加内存(使用-Xmx 选项)。 将文件分成更小的部分(需要格式良好的 XML)并在 XSLT 中分别处理每个部分。

但是 XSLT 不能帮助您选择 no。 2,因为要开始处理你的文件,它需要将它全部加载到内存中。它不能加载部分文本并将其拆分,因为每个片段都必须是格式正确的 XML。甚至 SAX 解析器也可能没有那么高效,因为您只有一个节点。您最好使用高效的字符串解析器,您可以在其中拆分 CSV,然后将每个片段包装在 XML 标记中。

【讨论】:

感谢楼主的回复。正如我在上面提到的,选项 2 似乎是唯一可行的解​​决方案。

以上是关于XSLT 1.0:CSV 到 XML - 如何分而治之的主要内容,如果未能解决你的问题,请参考以下文章

XSLT - XML 到 CSV 将列转换为具有两种不同类型地址的行

XML to csv 做正确的 xslt

使用 XSLT 将 XML 转换为多个 CSV

XML 到 CSV 使用 XSLT 帮助

使用 XSLT 节点值的 XML 到 CSV 未显示

使用 XSLT 将 CSV 文件转换为 XML