Как использовать XMLReader в PHP?
У меня есть следующий XML-файл, файл довольно большой, и я не смог заставить simplexml открыть и прочитать файл, поэтому я пытаюсь XMLReader без успеха в php
<?xml version="1.0" encoding="ISO-8859-1"?>
<products>
<last_updated>2009-11-30 13:52:40</last_updated>
<product>
<element_1>foo</element_1>
<element_2>foo</element_2>
<element_3>foo</element_3>
<element_4>foo</element_4>
</product>
<product>
<element_1>bar</element_1>
<element_2>bar</element_2>
<element_3>bar</element_3>
<element_4>bar</element_4>
</product>
</products>
к сожалению, я не нашел хорошего учебника по этому вопросу для PHP и хотел бы посмотреть, как я могу получить содержимое каждого элемента для хранения в базе данных.
7 ответов
все зависит от того, насколько велика единица работы, но я думаю, вы пытаетесь лечить каждого <product/>
узлы в последовательности.
для этого самый простой способ - использовать XMLReader для доступа к каждому узлу, а затем использовать SimpleXML для доступа к ним. Таким образом, вы сохраняете низкое использование памяти, потому что обрабатываете один узел за раз, и все еще используете простоту использования SimpleXML. Например:
$z = new XMLReader;
$z->open('data.xml');
$doc = new DOMDocument;
// move to the first <product /> node
while ($z->read() && $z->name !== 'product');
// now that we're at the right depth, hop to the next <product/> until the end of the tree
while ($z->name === 'product')
{
// either one should work
//$node = new SimpleXMLElement($z->readOuterXML());
$node = simplexml_import_dom($doc->importNode($z->expand(), true));
// now you can use $node without going insane about parsing
var_dump($node->element_1);
// go to next <product />
$z->next('product');
}
краткий обзор плюсов и минусов различных подходы:
только XMLReader
Плюсы: быстро, использует мало памяти
минусы: чрезмерно трудно писать и отлаживать, требует много кода userland, чтобы сделать что-нибудь полезное. Код Userland медленный и подвержен ошибкам. Кроме того, это оставляет вам больше строк кода для поддержания
XMLReader + SimpleXML
плюсы: не использует много памяти (только память, необходимая для обработки одного узла) и SimpleXML, как следует из названия, очень прост в использовании.
минусы: создание объекта SimpleXMLElement для каждого узла не очень быстро. Вы действительно должны проверить его, чтобы понять, является ли это проблемой для вас. Однако даже скромная машина могла бы обрабатывать тысячу узлов в секунду.
XMLReader + DOM
плюсы: использует примерно как много памяти как SimpleXML, и XMLReader:: expand () быстрее, чем создание нового SimpleXMLElement. Я хотел бы, чтобы это было возможно использовать
simplexml_import_dom()
но это, похоже, не работает в этом случаеминусы: DOM раздражает работать. Это на полпути между XMLReader и SimpleXML. Не так сложно и неудобно, как XMLReader, но световые годы от работы с SimpleXML.
мой совет: напишите прототип с помощью SimpleXML, посмотрите, работать для вас. Если производительность имеет первостепенное значение, попробуйте DOM. Держитесь как можно дальше от XMLReader. Помните, что чем больше кода Вы пишете, тем выше вероятность появления ошибок или регрессий производительности.
для xml, отформатированного с атрибутами...
данные.XML-код:
<building_data>
<building address="some address" lat="28.902914" lng="-71.007235" />
<building address="some address" lat="48.892342" lng="-75.0423423" />
<building address="some address" lat="58.929753" lng="-79.1236987" />
</building_data>
php код:
$reader = new XMLReader();
if (!$reader->open("data.xml")) {
die("Failed to open 'data.xml'");
}
while($reader->read()) {
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'building') {
$address = $reader->getAttribute('address');
$latitude = $reader->getAttribute('lat');
$longitude = $reader->getAttribute('lng');
}
$reader->close();
большая часть моей жизни синтаксического анализа XML тратится на извлечение самородков полезной информации из грузовиков XML (Amazon MWS). Таким образом, мой ответ предполагает, что вам нужна только конкретная информация, и вы знаете, где она находится.
Я нахожу самый простой способ использовать XMLReader, чтобы знать, какие теги я хочу информацию из и использовать их. Если вы знаете структуру XML и у него много уникальных тегов, я считаю, что использовать первый случай легко. Случаи 2 и 3 просто показать вам как это можно сделать для более сложных тегов. Это очень быстро; у меня есть обсуждение скорости на какой самый быстрый синтаксический анализатор XML в PHP?
самое главное помнить при выполнении синтаксического анализа на основе тегов, как это использовать if ($myXML->nodeType == XMLReader::ELEMENT) {...
- который проверяет, что мы имеем дело только с открывающими узлами, а не с пробелами или закрывающими узлами или что-то еще.
function parseMyXML ($xml) { //pass in an XML string
$myXML = new XMLReader();
$myXML->xml($xml);
while ($myXML->read()) { //start reading.
if ($myXML->nodeType == XMLReader::ELEMENT) { //only opening tags.
$tag = $myXML->name; //make $tag contain the name of the tag
switch ($tag) {
case 'Tag1': //this tag contains no child elements, only the content we need. And it's unique.
$variable = $myXML->readInnerXML(); //now variable contains the contents of tag1
break;
case 'Tag2': //this tag contains child elements, of which we only want one.
while($myXML->read()) { //so we tell it to keep reading
if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Amount') { // and when it finds the amount tag...
$variable2 = $myXML->readInnerXML(); //...put it in $variable2.
break;
}
}
break;
case 'Tag3': //tag3 also has children, which are not unique, but we need two of the children this time.
while($myXML->read()) {
if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Amount') {
$variable3 = $myXML->readInnerXML();
break;
} else if ($myXML->nodeType == XMLReader::ELEMENT && $myXML->name === 'Currency') {
$variable4 = $myXML->readInnerXML();
break;
}
}
break;
}
}
}
$myXML->close();
}
XMLReader хорошо документирован на сайт PHP. Это синтаксический анализатор XML, который означает, что он используется для итерации через узлы (или узлы DOM) данного XML-документа. Например, вы можете просмотреть весь документ, который вы дали, следующим образом:
<?php
$reader = new XMLReader();
if (!$reader->open("data.xml"))
{
die("Failed to open 'data.xml'");
}
while($reader->read())
{
$node = $reader->expand();
// process $node...
}
$reader->close();
?>
тогда вам решать, как обращаться с узлом, возвращаемым XMLReader:: expand ().
Simple example:
public function productsAction()
{
$saveFileName = 'ceneo.xml';
$filename = $this->path . $saveFileName;
if(file_exists($filename)) {
$reader = new XMLReader();
$reader->open($filename);
$countElements = 0;
while($reader->read()) {
if($reader->nodeType == XMLReader::ELEMENT) {
$nodeName = $reader->name;
}
if($reader->nodeType == XMLReader::TEXT && !empty($nodeName)) {
switch ($nodeName) {
case 'id':
var_dump($reader->value);
break;
}
}
if($reader->nodeType == XMLReader::END_ELEMENT && $reader->name == 'offer') {
$countElements++;
}
}
$reader->close();
exit(print('<pre>') . var_dump($countElements));
}
}
принятый ответ дал мне хорошее начало, но принес больше классов и больше обработки, чем мне хотелось бы; так что это моя интерпретация:
$xml_reader = new XMLReader;
$xml_reader->open($feed_url);
// move the pointer to the first product
while ($xml_reader->read() && $xml_reader->name != 'product');
// loop through the products
while ($xml_reader->name == 'product')
{
// load the current xml element into simplexml and we’re off and running!
$xml = simplexml_load_string($xml_reader->readOuterXML());
// now you can use your simpleXML object ($xml).
echo $xml->element_1;
// move the pointer to the next product
$xml_reader->next('product');
}
// don’t forget to close the file
$xml_reader->close();
эта тема давно закрыта, но я только что ее нашел. слава Богу.
моя проблема в том, что я должен прочитать файл ONIX (данные книги) и сохранить его в нашей базе данных. Я использую simplexml_load раньше, и хотя он использовал много памяти, но все же нормально для относительно небольшого файла (до 300 МБ). За пределами этого размера для меня катастрофа.
после чтения, особенно интерпретации Фрэнсиса Льюиса, я использую комбинацию xmlreader и simplexml. Результат исключительный, использование памяти небольшое и вставить его в базу данных достаточно быстро для меня.
вот мой код:
<?php
$dbhost = "localhost"; // mysql host
$dbuser = ""; //mysql username
$dbpw = ""; // mysql user password
$db = ""; // mysql database name
//i need to truncate the old data first
$conn2 = mysql_connect($dbhost, $dbuser, $dbpw);
mysql_select_db($db);
mysql_query ("truncate ebiblio",$conn2);
//$xmlFile = $_POST['xmlFile'];
//$xml=simplexml_load_file("ebiblio.xml") or die("Error: Cannot create object");
$reader = new XMLReader();
//load the selected XML file to the DOM
if (!$reader->open("ebiblio.xml")) {
die("Failed to open 'ebiblio.xml'");
}
while ($reader->read()):
if ($reader->nodeType == XMLReader::ELEMENT && $reader->name == 'product'){
$xml = simplexml_load_string($reader->readOuterXML());
$productcode = (string)$xml->a001;
$title = (string)$xml->title->b203;
$author = (string)$xml->contributor->b037;
$language = (string)$xml->language->b252;
$category = $xml->subject->b069;
$description = (string)$xml->othertext->d104;
$publisher = (string)$xml->publisher->b081;
$pricecover = (string)$xml->supplydetail->price->j151;
$salesright = (string)$xml->salesrights->b090;
@$productcode1 = htmlentities($productcode,ENT_QUOTES,'latin1_swedish_ci');
@$title1 = htmlentities($title,ENT_QUOTES,'latin1_swedish_ci');
@$author1 = htmlentities($author,ENT_QUOTES,'latin1_swedish_ci');
@$language1 = htmlentities($language,ENT_QUOTES,'latin1_swedish_ci');
@$category1 = htmlentities($category,ENT_QUOTES,'latin1_swedish_ci');
@$description1 = htmlentities($description,ENT_QUOTES,'latin1_swedish_ci');
@$publisher1 = htmlentities($publisher,ENT_QUOTES,'latin1_swedish_ci');
@$pricecover1 = htmlentities($pricecover,ENT_QUOTES,'latin1_swedish_ci');
@$salesright1 = htmlentities($salesright,ENT_QUOTES,'latin1_swedish_ci');
$conn = mysql_connect($dbhost, $dbuser, $dbpw);
mysql_select_db($db);
$sql = "INSERT INTO ebiblio VALUES ('" . $productcode1 . "','" . $title1 . "','" . $author1 . "','" . $language1 . "','" . $category1 . "','" . $description1 . "','" . $publisher1 . "','" . $pricecover1 . "','" . $salesright1 . "')";
mysql_query($sql, $conn);
$reader->next('product');
}
endwhile;
?>