簡體   English   中英

將巨型XML文件拆分為n個子版本

[英]Split giant XML file into n-child versions

例如,巨型文件有5000萬行,例如:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<root>
  <activity>
    <deliv>
      <subitem1>text</subitem1>
      <subitem2>text</subitem2>
    </deliv>
    <deliv>
      <subitem1>text</subitem1>
      <subitem2>text</subitem2>
    </deliv>
    <deliv>
      <subitem1>text</subitem1>
      <subitem2>text</subitem2>
    </deliv>
  </activity>
</root>

每個“子”文件都將具有相同的結構,但大約為500萬行,即原始文件的1/10。

這樣做的原因是在不消耗內存(SQL Server的OPENXML)的情況下,更易於管理將其導入數據庫。

XSLT是這里的最佳選擇嗎?

XSLT-2.0及更高版本非常適合此任務。
XSLT-3.0甚至支持流。

以下樣式表使用xsl:result-document將XML文件拆分為可配置的文件數量。

它帶有兩個參數:

  • split每個拆分中的項目數
  • doc源文檔的名稱

這是為您的示例( split.xslt )定制的XSLT-2.0模板:

<?xml version="1.0"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xsl:param name="split" select="2" />        <!-- number of entries in each split -->
  <xsl:param name="doc" select="'src.xml'" />  <!-- name of source XML --> 

  <xsl:template match="/">  
    <xsl:variable name="cnt" select="xs:integer(count(document($doc)/root/activity/deliv) div xs:integer($split))" />    
    <xsl:value-of select="concat('#',$cnt,'#')" />
    <xsl:for-each select="0 to $cnt">
        <xsl:variable name="cur" select="xs:integer(.)" /> 
        <xsl:result-document method="xml" href="output_no_{$cur}.xml" exclude-result-prefixes="xs">
            <root>
                <activity>
                    <xsl:for-each select="document($doc)/root/activity/deliv[position() gt (xs:integer($split) * $cur) and position() le (xs:integer($split) * ($cur + 1))]">
                        <xsl:copy-of select="."/>
                    </xsl:for-each>
                </activity>
            </root>
        </xsl:result-document>
    </xsl:for-each>
  </xsl:template>

</xsl:stylesheet> 

Saxon的當前版本中,您可以這樣稱呼它:

java -jar saxon9he.jar -xsl:split.xslt src.xml doc=src.xml split=2

Saxon 9.8(Saxon 9.8 EE)企業版支持具有一年歷史的XSLT 3.0規范的流功能,該功能使您可以使用XSLT的子集以向前方式僅通過必需的內存來讀取XML文檔。存儲當前訪問的節點及其祖先。

使用這種方法,您可以編寫諸如for-each-group select="activity/deliv" group-adjacent="(position() - 1) idiv $size"來進行位置分組,從而通過deliv元素讀取文件deliv並將它們分成$size組:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:param name="size" as="xs:integer" select="1000"/>

    <xsl:mode on-no-match="shallow-copy" streamable="yes"/>

    <xsl:template match="root">
        <xsl:for-each-group select="activity/deliv" group-adjacent="(position() - 1) idiv $size">
            <xsl:result-document href="split-{format-number(current-grouping-key() + 1, '00000')}.xml" indent="yes">
                <root>
                    <activity>
                        <xsl:copy-of select="current-group()"/>
                    </activity>
                </root>
            </xsl:result-document>
        </xsl:for-each-group>
    </xsl:template>

</xsl:stylesheet>

這會將您的輸入拆分為多個文件,每個文件包含$size deliv元素(如果剩下的少於$size則分別為剩余的deliv元素的最后一個)。

使用Saxon EE需要獲得商業許可證,但存在試用許可證。

XSLT可以完成這項工作。 我建議您動手使用XSLT v2.0處理器,以便可以使用xsl:result-document。 然后,您需要一點邏輯來決定何時在文件之間分割。 您可以以deliv元素的position()為基礎,或者嘗試使用xsl:for-each-group來創建發送到每個文件的組。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM