使用 XMLreader 读取和解析大型 XML 文件。空值问题
Using XMLreader to read and parse large XML files. Empty values problem
我需要阅读 XML 个大约 1 GB 的文件。我的 XML:
<products>
<product>
<categoryName>Kable i konwertery AV</categoryName>
<brandName>Belkin</brandName>
<productCode>AV10176bt1M-BLK</productCode>
<productId>5616488</productId>
<productFullName>Kabel Belkin Kabel HDMI Ultra HD High Speed 1m-AV10176bt1M-BLK</productFullName>
<productEan>0745883767465</productEan>
<productEuroPriceNetto>59.71</productEuroPriceNetto>
<productFrontendPriceNetto>258.54</productFrontendPriceNetto>
<productFastestSupplierQuantity>23</productFastestSupplierQuantity>
<deliveryEstimatedDays>2</deliveryEstimatedDays>
</product>
<product>
<categoryName>Telewizory</categoryName>
<brandName>Sony</brandName>
<productCode>KDL32WD757SAEP</productCode>
<productId>1005662</productId>
<productFullName>Telewizor Sony KDL-32WD757 SAEP</productFullName>
<productEan></productEan>
<productEuroPriceNetto>412.33</productEuroPriceNetto>
<productFrontendPriceNetto>1785.38</productFrontendPriceNetto>
<productFastestSupplierQuantity>11</productFastestSupplierQuantity>
<deliveryEstimatedDays>6</deliveryEstimatedDays>
</product>
<product>
<categoryName>Kuchnie i akcesoria</categoryName>
<brandName>Brimarex</brandName>
<productCode>1566287</productCode>
<productId>885156</productId>
<productFullName>Brimarex Drewniane owoce, Kiwi - 1566287</productFullName>
<productEan></productEan>
<productEuroPriceNetto>0.7</productEuroPriceNetto>
<productFrontendPriceNetto>3.05</productFrontendPriceNetto>
<productFastestSupplierQuantity>7</productFastestSupplierQuantity>
<deliveryEstimatedDays>3</deliveryEstimatedDays>
</product>
</products>
我用XMLreader.
$reader = new XMLReader();
$reader->open($url);
$count = 0;
while($reader->read()) {
if($reader->nodeType == XMLReader::ELEMENT)
$nodeName = $reader->name;
if(($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA)) {
if ($nodeName == 'categoryName') $categoryName = $reader->value;
if ($nodeName == 'brandName') $brandName = $reader->value;
if ($nodeName == 'productCode') $productCode = $reader->value;
if ($nodeName == 'productId') $productId = $reader->value;
if ($nodeName == 'productFullName') $productFullName = $reader->value;
if ($nodeName == 'productEan') $productEan = $reader->value;
if ($nodeName == 'productEuroPriceNetto') $productEuroPriceNetto = $reader->value;
if ($nodeName == 'productFastestSupplierQuantity') $productFastestSupplierQuantity = $reader->value;
if ($nodeName == 'deliveryEstimatedDays') $deliveryEstimatedDays = $reader->value;
}
if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'product') {
$count++;
}
}
$reader->close();
除一个问题外一切正常...当缺少某些值时,例如输出中的 <productEan></productEan>
我从前一个标签中获取一个值,而不是空标签直到另一个不为空的标签。
例如,如果前一个节点像示例 <productEan>0745883767465</productEan>
中的那样,另外两个 <productEan></productEan>
在输出数组中为空,我得到相同的值,0745883767465
。
解决这个问题的正确方法是什么?或者也许有人有可行的解决方案...
在每个循环中重置所有变量。好像你不给它赋值,它就是取之前赋值。
<?php
while($reader->read()) {
$categoryName =
$brandName =
$productCode =
$productId =
$productFullName =
$productEan =
$productEuroPriceNetto =
$productFastestSupplierQuantity =
$deliveryEstimatedDays = '';
//... code
}
?>
这里有一些代码可以满足您的需求。它在遇到 TEXT
或 CDATA
节点时保存每个元素的值,然后在遇到 END_ELEMENT
时存储它。那时保存的值设置为 ''
,这样如果没有找到元素的值,它会得到一个空字符串(如果您愿意,可以将其更改为 null
)。它还处理自闭合标签,例如 <brandName />
,并在找到 ELEMENT
节点时进行 isEmptyElement
检查。它利用 PHPs 可变变量来避免代码中的 if ($nodename == ...)
长序列,而且还使用数组来存储每个产品的值,从长远来看,我认为这是解决问题的更好方法.
$reader = new XMLReader();
$reader->xml($xml);
$count = 0;
$this_value = '';
$products = array();
while($reader->read()) {
switch ($reader->nodeType) {
case XMLReader::ELEMENT:
// deal with self-closing tags e.g. <productEan />
if ($reader->isEmptyElement) {
${$reader->name} = '';
$products[$count][$reader->name] = '';
}
break;
case XMLReader::TEXT:
case XMLReader::CDATA:
// save the value for storage when we get to the end of the element
$this_value = $reader->value;
break;
case XMLReader::END_ELEMENT:
if ($reader->name == 'product') {
$count++;
print_r(array($categoryName, $brandName, $productCode, $productId, $productFullName, $productEan, $productEuroPriceNetto, $productFrontendPriceNetto, $productFastestSupplierQuantity, $deliveryEstimatedDays));
}
elseif ($reader->name != 'products') {
${$reader->name} = $this_value;
$products[$count][$reader->name] = $this_value;
// set this_value to a blank string to allow for empty tags
$this_value = '';
}
break;
case XMLReader::WHITESPACE:
case XMLReader::SIGNIFICANT_WHITESPACE:
default:
// nothing to do
break;
}
}
$reader->close();
print_r($products);
我省略了输出,因为它很长,但您可以在 demo on 3v4l.org.
中看到运行中的代码
如果您不使用单个值,而是将这些值存储在一个详细信息数组中,则可以在处理完每个元素后清空该数组...
$reader->open($url);
$count = 0;
$data = [];
while($reader->read()) {
if($reader->nodeType == XMLReader::ELEMENT)
$nodeName = $reader->name;
if(($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA)) {
$data[$nodeName] = $reader->value;
}
if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'product') {
// Process data
echo ($data['productEan']??"Empty").PHP_EOL;
// Reset
$data = [];
$count++;
}
}
$reader->close();
你的测试数据给出了...
0745883767465
Empty
Empty
我需要阅读 XML 个大约 1 GB 的文件。我的 XML:
<products>
<product>
<categoryName>Kable i konwertery AV</categoryName>
<brandName>Belkin</brandName>
<productCode>AV10176bt1M-BLK</productCode>
<productId>5616488</productId>
<productFullName>Kabel Belkin Kabel HDMI Ultra HD High Speed 1m-AV10176bt1M-BLK</productFullName>
<productEan>0745883767465</productEan>
<productEuroPriceNetto>59.71</productEuroPriceNetto>
<productFrontendPriceNetto>258.54</productFrontendPriceNetto>
<productFastestSupplierQuantity>23</productFastestSupplierQuantity>
<deliveryEstimatedDays>2</deliveryEstimatedDays>
</product>
<product>
<categoryName>Telewizory</categoryName>
<brandName>Sony</brandName>
<productCode>KDL32WD757SAEP</productCode>
<productId>1005662</productId>
<productFullName>Telewizor Sony KDL-32WD757 SAEP</productFullName>
<productEan></productEan>
<productEuroPriceNetto>412.33</productEuroPriceNetto>
<productFrontendPriceNetto>1785.38</productFrontendPriceNetto>
<productFastestSupplierQuantity>11</productFastestSupplierQuantity>
<deliveryEstimatedDays>6</deliveryEstimatedDays>
</product>
<product>
<categoryName>Kuchnie i akcesoria</categoryName>
<brandName>Brimarex</brandName>
<productCode>1566287</productCode>
<productId>885156</productId>
<productFullName>Brimarex Drewniane owoce, Kiwi - 1566287</productFullName>
<productEan></productEan>
<productEuroPriceNetto>0.7</productEuroPriceNetto>
<productFrontendPriceNetto>3.05</productFrontendPriceNetto>
<productFastestSupplierQuantity>7</productFastestSupplierQuantity>
<deliveryEstimatedDays>3</deliveryEstimatedDays>
</product>
</products>
我用XMLreader.
$reader = new XMLReader();
$reader->open($url);
$count = 0;
while($reader->read()) {
if($reader->nodeType == XMLReader::ELEMENT)
$nodeName = $reader->name;
if(($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA)) {
if ($nodeName == 'categoryName') $categoryName = $reader->value;
if ($nodeName == 'brandName') $brandName = $reader->value;
if ($nodeName == 'productCode') $productCode = $reader->value;
if ($nodeName == 'productId') $productId = $reader->value;
if ($nodeName == 'productFullName') $productFullName = $reader->value;
if ($nodeName == 'productEan') $productEan = $reader->value;
if ($nodeName == 'productEuroPriceNetto') $productEuroPriceNetto = $reader->value;
if ($nodeName == 'productFastestSupplierQuantity') $productFastestSupplierQuantity = $reader->value;
if ($nodeName == 'deliveryEstimatedDays') $deliveryEstimatedDays = $reader->value;
}
if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'product') {
$count++;
}
}
$reader->close();
除一个问题外一切正常...当缺少某些值时,例如输出中的 <productEan></productEan>
我从前一个标签中获取一个值,而不是空标签直到另一个不为空的标签。
例如,如果前一个节点像示例 <productEan>0745883767465</productEan>
中的那样,另外两个 <productEan></productEan>
在输出数组中为空,我得到相同的值,0745883767465
。
解决这个问题的正确方法是什么?或者也许有人有可行的解决方案...
在每个循环中重置所有变量。好像你不给它赋值,它就是取之前赋值。
<?php
while($reader->read()) {
$categoryName =
$brandName =
$productCode =
$productId =
$productFullName =
$productEan =
$productEuroPriceNetto =
$productFastestSupplierQuantity =
$deliveryEstimatedDays = '';
//... code
}
?>
这里有一些代码可以满足您的需求。它在遇到 TEXT
或 CDATA
节点时保存每个元素的值,然后在遇到 END_ELEMENT
时存储它。那时保存的值设置为 ''
,这样如果没有找到元素的值,它会得到一个空字符串(如果您愿意,可以将其更改为 null
)。它还处理自闭合标签,例如 <brandName />
,并在找到 ELEMENT
节点时进行 isEmptyElement
检查。它利用 PHPs 可变变量来避免代码中的 if ($nodename == ...)
长序列,而且还使用数组来存储每个产品的值,从长远来看,我认为这是解决问题的更好方法.
$reader = new XMLReader();
$reader->xml($xml);
$count = 0;
$this_value = '';
$products = array();
while($reader->read()) {
switch ($reader->nodeType) {
case XMLReader::ELEMENT:
// deal with self-closing tags e.g. <productEan />
if ($reader->isEmptyElement) {
${$reader->name} = '';
$products[$count][$reader->name] = '';
}
break;
case XMLReader::TEXT:
case XMLReader::CDATA:
// save the value for storage when we get to the end of the element
$this_value = $reader->value;
break;
case XMLReader::END_ELEMENT:
if ($reader->name == 'product') {
$count++;
print_r(array($categoryName, $brandName, $productCode, $productId, $productFullName, $productEan, $productEuroPriceNetto, $productFrontendPriceNetto, $productFastestSupplierQuantity, $deliveryEstimatedDays));
}
elseif ($reader->name != 'products') {
${$reader->name} = $this_value;
$products[$count][$reader->name] = $this_value;
// set this_value to a blank string to allow for empty tags
$this_value = '';
}
break;
case XMLReader::WHITESPACE:
case XMLReader::SIGNIFICANT_WHITESPACE:
default:
// nothing to do
break;
}
}
$reader->close();
print_r($products);
我省略了输出,因为它很长,但您可以在 demo on 3v4l.org.
中看到运行中的代码如果您不使用单个值,而是将这些值存储在一个详细信息数组中,则可以在处理完每个元素后清空该数组...
$reader->open($url);
$count = 0;
$data = [];
while($reader->read()) {
if($reader->nodeType == XMLReader::ELEMENT)
$nodeName = $reader->name;
if(($reader->nodeType == XMLReader::TEXT || $reader->nodeType == XMLReader::CDATA)) {
$data[$nodeName] = $reader->value;
}
if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'product') {
// Process data
echo ($data['productEan']??"Empty").PHP_EOL;
// Reset
$data = [];
$count++;
}
}
$reader->close();
你的测试数据给出了...
0745883767465
Empty
Empty