使用 DomDocument 将 XML 分割成更小的文件
Split XML into smaller files using 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");
感谢任何建议。
考虑动态 XSLT where PHP passes multiples of 100 to parse the range of nodes in the source XML into smaller outputs using XSLT's position()
。具体来说,PHP 将循环变量作为参数传递到绑定到 $splitnum
的 XSLT(非常类似于 SQL 参数化)。
输入 (假设XML结构像你之前的post)
<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 XMLs,每个具有连续的 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);
}
}
使用 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);
}
这不是最优雅的代码,但它也可以构成一个程序的基础,一个一个地处理元素,而不是一次加载整个文档。
我正在尝试处理一个大 (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");
感谢任何建议。
考虑动态 XSLT where PHP passes multiples of 100 to parse the range of nodes in the source XML into smaller outputs using XSLT's position()
。具体来说,PHP 将循环变量作为参数传递到绑定到 $splitnum
的 XSLT(非常类似于 SQL 参数化)。
输入 (假设XML结构像你之前的post)
<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 XMLs,每个具有连续的 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);
}
}
使用 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);
}
这不是最优雅的代码,但它也可以构成一个程序的基础,一个一个地处理元素,而不是一次加载整个文档。