使用DomDocument将XML拆分为较小的文件

时间:2017-11-08 20:56:14

标签: php xml domdocument

我正在尝试处理大型(20mb +)XML文件,但由于其大小而无法解析它。我想把它分解成更小的段,比如每个文件100个属性节点。

目前我正在使用下面的代码根据其他条件分离文件,但我有点不确定如何调整代码以执行每个文件拆分的100条记录:

$destination = new DOMDocument;
$destination->preserveWhiteSpace = true;
$destination->loadXML('<?xml version="1.0" encoding="utf-8"?><root></root>');

$source = new DOMDocument;
$source->load('bes1c8ca168f910.xml');

$xp = new DOMXPath($source);
$destRoot = $destination->getElementsByTagName("root")->item(0);

foreach ($xp->query('/root/property[rent]') as $item) {
    $newItem = $destination->importNode($item, true);
    $destRoot->appendChild($newItem);
    $item->parentNode->removeChild($item);
}

$source->save("sales.xml");
$destination->formatOutput = true;
$destination->save("rentals.xml");

感谢任何建议。

2 个答案:

答案 0 :(得分:1)

考虑动态XSLT,其中PHP传递100的倍数,使用XSLT position()将源XML中的节点范围解析为较小的输出。具体来说,PHP将循环变量作为参数传递给绑定到$splitnum的XSLT(非常类似于SQL参数化)。

输入 (假设您的上一篇文章的XML结构)

<root>
    <property>
      <rent>
        <term>short</term>
        <freq>week</freq>
        <price_peak>5845</price_peak>
        <price_high>5845</price_high>
        <price_medium>4270</price_medium>
        <price_low>3150</price_low>
      </rent>
    </property>
    <property>
      <rent>
        <term>long</term>
        <freq>week</freq>
        <price_peak>6845</price_peak>
        <price_high>6845</price_high>
        <price_medium>4270</price_medium>
        <price_low>3150</price_low>
      </rent>
    </property>
    ...
</root>

XSLT

(另存为.xsl文件,一个特殊的.xml文件;脚本需要传入参数以进行节点范围拆分)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
   <xsl:output method="xml" omit-xml-declaration="yes" indent="yes" />
   <xsl:strip-space elements="*" />

   <xsl:param name="splitnum" />

   <xsl:template match="/root">
      <xsl:copy>
         <xsl:variable name="currsplit" select="$splitnum - 99"/>
         <xsl:apply-templates select="property[position() &gt;= $currsplit and 
                                               position() &lt;= $splitnum]" />
      </xsl:copy>
   </xsl:template>

   <xsl:template match="property">
      <xsl:copy>
         <xsl:copy-of select="*" />
      </xsl:copy>
   </xsl:template>

</xsl:stylesheet>

PHP

(将循环迭代器变量作为参数传递给XSLT;生成10个XML,每个XML具有连续的100个属性节点,根据需要扩展限制为1000)

// Load XML and XSL
$xml = new DOMDocument;
$xml->load('Input.xml');

$xsl = new DOMDocument;
$xsl->load($xslstr);

$prop_total = $xml->getElementsByTagName('property')->length + 100;

for($i=1; $i<=$prop_total; $i++){
  if ($i % 100 == 0) {         
    // Configure transformer
    $proc = new XSLTProcessor;
    $proc->importStyleSheet($xsl);

    // Binds loop variable to XSLT parameter
    $proc->setParameter('', 'splitnum', $i);

    // Transform XML source
    $newXML = new DOMDocument;
    $newXML = $proc->transformToXML($xml);

    // Output file
    file_put_contents('rentals_'.$i.'.xml', $newXML);
  }
}

答案 1 :(得分:0)

使用XMLReader拆分文件的示例。我试图使其灵活,因此文件名用作正在创建的文件的基础,并且拆分计数被定义为变量。

代码的主要部分是一个读取<property>元素的循环,您可以根据需要调整它。我还使用$rootNodeName作为占位符,无论你的根节点被调用。

$fileName = "data/t1.xml";
$original = new XMLReader;
$original->open($fileName);
$path_parts = pathinfo($fileName);
$filePrefix = $path_parts['dirname'].'/'.$path_parts['filename'].'-';
$nextRecord = 0;
$splitCount = 2;
$rootNodeName = "data";

$doc = new DOMDocument();
$doc->loadXML("<$rootNodeName/>");
while ($original->read() && $original->name !== 'property');
while ($original->name === 'property')
{
    $newNode = $doc->importNode($original->expand(), true);
    $doc->documentElement->appendChild($newNode);
    $nextRecord++;

    if ( $nextRecord % $splitCount == 0 )   {
        $nextFileName = $filePrefix.$nextRecord.".".$path_parts['extension'];
        $doc->save($nextFileName);
        $doc = new DOMDocument();
        $doc->loadXML("<$rootNodeName/>");
    }
    $original->next('property');
}
if ( $nextRecord % $splitCount != 0 )   {
    $nextFileName = $filePrefix.$nextRecord.".".$path_parts['extension'];
    $doc->save($nextFileName);
}

它不是最优雅的代码,但它也可以构成一个程序的基础,逐个处理元素而不是一次加载整个文档。