Cách sử dụng XMLReader trong PHP?


79

Tôi có tệp XML sau, tệp này khá lớn và tôi không thể lấy simplexml để mở và đọc tệp, vì vậy tôi đang thử XMLReader nhưng không thành công trong 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>

Rất tiếc, tôi đã không tìm thấy một hướng dẫn tốt về điều này cho PHP và rất muốn xem cách tôi có thể lấy từng nội dung phần tử để lưu trữ trong cơ sở dữ liệu.


1
Bạn đã đọc một số ví dụ do người dùng đóng góp trong tài liệu PHP chưa? php.net/manual/en/class.xmlreader.php#61929 có thể hữu ích.
mcrumley

Câu trả lời:


224

Tất cả phụ thuộc vào mức độ lớn của đơn vị công việc, nhưng tôi đoán bạn đang cố gắng xử lý từng <product/>nút liên tiếp.

Đối với điều đó, cách đơn giản nhất là sử dụng XMLReader để truy cập từng nút, sau đó sử dụng SimpleXML để truy cập chúng. Bằng cách này, bạn giữ cho việc sử dụng bộ nhớ ở mức thấp vì bạn đang xử lý một nút tại một thời điểm và bạn vẫn tận dụng được tính dễ sử dụng của SimpleXML. Ví dụ:

$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');
}

Tổng quan nhanh về ưu và nhược điểm của các cách tiếp cận khác nhau:

Chỉ XMLReader

  • Ưu điểm: nhanh, sử dụng ít bộ nhớ

  • Nhược điểm: quá khó để viết và gỡ lỗi, yêu cầu nhiều mã vùng người dùng để làm bất cứ điều gì hữu ích. Mã userland chậm và dễ bị lỗi. Thêm vào đó, nó để lại cho bạn nhiều dòng mã hơn để duy trì

XMLReader + SimpleXML

  • Ưu điểm: không sử dụng nhiều bộ nhớ (chỉ bộ nhớ cần thiết để xử lý một nút) và SimpleXML, như tên của nó, thực sự dễ sử dụng.

  • Nhược điểm: tạo một đối tượng SimpleXMLElement cho mỗi nút không nhanh lắm. Bạn thực sự phải đánh giá nó để hiểu liệu đó có phải là vấn đề đối với bạn hay không. Mặc dù vậy, ngay cả một cỗ máy khiêm tốn cũng có thể xử lý một nghìn nút mỗi giây.

XMLReader + DOM

  • Ưu điểm: sử dụng nhiều bộ nhớ như SimpleXML và XMLReader :: expand () nhanh hơn việc tạo SimpleXMLElement mới. Tôi ước nó có thể sử dụng simplexml_import_dom()nhưng nó dường như không hoạt động trong trường hợp đó

  • Nhược điểm: DOM gây khó chịu khi làm việc với. Nó nằm giữa XMLReader và SimpleXML. Không phức tạp và khó xử như XMLReader, nhưng sẽ mất vài năm nữa để làm việc với SimpleXML.

Lời khuyên của tôi: hãy viết một nguyên mẫu với SimpleXML, xem nó có phù hợp với bạn không. Nếu hiệu suất là tối quan trọng, hãy thử DOM. Tránh xa XMLReader nhất có thể. Hãy nhớ rằng bạn càng viết nhiều mã, khả năng bạn đưa ra lỗi hoặc giới thiệu hồi quy hiệu suất càng cao.


1
có cách nào để làm điều này hoàn toàn với XMLReader hay không có lợi thế?
Shadi Almosri

2
Bạn có thể làm điều đó hoàn toàn với XMLReader. Ưu điểm là nó sẽ nhanh hơn và sử dụng ít bộ nhớ hơn một chút. Điểm bất lợi là nó sẽ mất nhiều thời gian hơn để viết và khó gỡ lỗi hơn rất nhiều.
Josh Davis

2
Tại sao bạn không sử dụng $ z-> next ('product') khi chuyển đến nút sản phẩm đầu tiên?
redolent 19/09/12

Tôi không nhớ mã cụ thể đó, xin lỗi. Nếu tôi không thêm bất kỳ ghi chú nào về nó, có thể tôi đã bỏ qua khả năng.
Josh Davis

1
Hầu hết phân tích cú pháp dựa trên XMLReader có thể được thể hiện / gói vào mẫu trình vòng lặp. Tôi đã biên soạn một số lặp hữu ích và các bộ lọc cho rằng: git.io/xmlreaderiterator ( ý chính )
hakre

10

Đối với xml được định dạng với các thuộc tính ...

data.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>

mã 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();

1
Mặc dù mã dài hơn rất nhiều và cách thủ công để xem qua XML, điều này sẽ giúp bạn tỉnh táo hơn, vì DOMDocument và SimpleXML có xu hướng giúp bạn đoán xem những gì sẽ được trả về.
b01

6

Câu trả lời được chấp nhận đã mang lại cho tôi một khởi đầu tốt, nhưng mang lại nhiều lớp hơn và nhiều quá trình xử lý hơn những gì tôi muốn; vì vậy đây là cách giải thích của tôi:

$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();

6

Phần lớn cuộc đời phân tích cú pháp XML của tôi dành để trích xuất các gói thông tin hữu ích từ các xe tải XML (Amazon MWS). Như vậy, câu trả lời của tôi giả định rằng bạn chỉ muốn thông tin cụ thể và bạn biết nó nằm ở đâu.

Tôi thấy cách dễ nhất để sử dụng XMLReader là biết tôi muốn lấy thông tin từ thẻ nào và sử dụng chúng. Nếu bạn biết cấu trúc của XML và nó có rất nhiều thẻ duy nhất, tôi thấy rằng việc sử dụng trường hợp đầu tiên là dễ dàng. Trường hợp 2 và 3 chỉ để cho bạn biết cách thực hiện đối với các thẻ phức tạp hơn. Điều này là cực kỳ nhanh chóng; Tôi có một cuộc thảo luận về tốc độ về Trình phân tích cú pháp XML nhanh nhất trong PHP là gì?

Điều quan trọng nhất cần nhớ khi thực hiện phân tích cú pháp dựa trên thẻ như thế này là sử dụng if ($myXML->nodeType == XMLReader::ELEMENT) {...- điều này sẽ kiểm tra để đảm bảo rằng chúng ta chỉ xử lý các nút đang mở chứ không phải khoảng trắng hoặc các nút đóng hoặc bất cứ điều gì.

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();
}

2
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));
    }
}

2

XMLReader được ghi lại trên trang PHP . Đây là một trình phân tích cú pháp kéo XML, có nghĩa là nó được sử dụng để lặp qua các nút (hoặc Nút DOM) của tài liệu XML đã cho. Ví dụ: bạn có thể xem qua toàn bộ tài liệu bạn đã cung cấp như sau:

<?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();
?>

Sau đó, tùy thuộc vào bạn để quyết định cách xử lý nút được trả về bởi XMLReader :: expand () .


bạn sẽ làm thế nào để nó chuyển sang nút tiếp theo sau khi nó xử lý xong một nút?
Shadi Almosri

24
Cũng liên quan đến việc XMLReader được tài liệu tốt trên php.net, tôi sẽ không đồng ý, đó là một trong những chức năng được tài liệu hóa tồi tệ nhất mà tôi từng thấy và tôi đã sử dụng php.net trong một thời gian dài và đó là nơi đầu tiên tôi hướng đến để giải quyết vấn đề này trước đây hỏi ở đây :)
Shadi Almosri

Tôi không chắc bạn hiểu cách XMLReader :: read () đi từ nút này sang nút khác. Lớp XMLReader cũng sử dụng libxml, một thư viện nổi tiếng cũng có sẵn cho PHP nếu bạn muốn xem qua.
Percutio

12
Ý tưởng rằng XMLReader được ghi chép đầy đủ là điều vô nghĩa. Vấn đề là nếu bạn không biết bắt đầu từ đâu, nó sẽ không cho bạn biết ở đâu cả: việc đưa ra một danh sách các phương thức lớp sẽ vô dụng nếu bạn không có ý tưởng đầu tiên về phương thức nào cần gọi.
xgretsch

0

Điều này hoạt động tốt hơn và nhanh hơn cho tôi


<html>
<head>
<script>
function showRSS(str) {
  if (str.length==0) {
    document.getElementById("rssOutput").innerHTML="";
    return;
  }
  if (window.XMLHttpRequest) {
    // code for IE7+, Firefox, Chrome, Opera, Safari
    xmlhttp=new XMLHttpRequest();
  } else {  // code for IE6, IE5
    xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
  xmlhttp.onreadystatechange=function() {
    if (this.readyState==4 && this.status==200) {
      document.getElementById("rssOutput").innerHTML=this.responseText;
    }
  }
  xmlhttp.open("GET","getrss.php?q="+str,true);
  xmlhttp.send();
}
</script>
</head>
<body>

<form>
<select onchange="showRSS(this.value)">
<option value="">Select an RSS-feed:</option>
<option value="Google">Google News</option>
<option value="ZDN">ZDNet News</option>
<option value="job">Job</option>
</select>
</form>
<br>
<div id="rssOutput">RSS-feed will be listed here...</div>
</body>
</html> 

** Tệp phụ trợ **


<?php
//get the q parameter from URL
$q=$_GET["q"];

//find out which feed was selected
if($q=="Google") {
  $xml=("http://news.google.com/news?ned=us&topic=h&output=rss");
} elseif($q=="ZDN") {
  $xml=("https://www.zdnet.com/news/rss.xml");
}elseif($q == "job"){
  $xml=("https://ngcareers.com/feed");
}

$xmlDoc = new DOMDocument();
$xmlDoc->load($xml);

//get elements from "<channel>"
$channel=$xmlDoc->getElementsByTagName('channel')->item(0);
$channel_title = $channel->getElementsByTagName('title')
->item(0)->childNodes->item(0)->nodeValue;
$channel_link = $channel->getElementsByTagName('link')
->item(0)->childNodes->item(0)->nodeValue;
$channel_desc = $channel->getElementsByTagName('description')
->item(0)->childNodes->item(0)->nodeValue;

//output elements from "<channel>"
echo("<p><a href='" . $channel_link
  . "'>" . $channel_title . "</a>");
echo("<br>");
echo($channel_desc . "</p>");

//get and output "<item>" elements
$x=$xmlDoc->getElementsByTagName('item');

$count = $x->length;

// print_r( $x->item(0)->getElementsByTagName('title')->item(0)->nodeValue);
// print_r( $x->item(0)->getElementsByTagName('link')->item(0)->nodeValue);
// print_r( $x->item(0)->getElementsByTagName('description')->item(0)->nodeValue);
// return;

for ($i=0; $i <= $count; $i++) {
  //Title
  $item_title = $x->item(0)->getElementsByTagName('title')->item(0)->nodeValue;
  //Link
  $item_link = $x->item(0)->getElementsByTagName('link')->item(0)->nodeValue;
  //Description
  $item_desc = $x->item(0)->getElementsByTagName('description')->item(0)->nodeValue;
  //Category
  $item_cat = $x->item(0)->getElementsByTagName('category')->item(0)->nodeValue;


  echo ("<p>Title: <a href='" . $item_link
  . "'>" . $item_title . "</a>");
  echo ("<br>");
  echo ("Desc: ".$item_desc);
   echo ("<br>");
  echo ("Category: ".$item_cat . "</p>");
}
?> 

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.