我正在尝试处理大型(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");
感谢任何建议。
答案 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() >= $currsplit and
position() <= $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);
}
它不是最优雅的代码,但它也可以构成一个程序的基础,逐个处理元素而不是一次加载整个文档。