2009-12-02 29 views
65

Tôi đã tập tin XML sau, các tập tin là khá lớn và tôi đã không thể để có được SimpleXML để mở và đọc file vì vậy tôi đang cố gắng XMLReader không có thành công trong phpLàm thế nào để sử dụng XMLReader 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> 

tôi đã không may không tìm thấy một hướng dẫn tốt về điều này cho PHP và rất thích xem làm thế nào tôi có thể nhận được mỗi nội dung phần tử để lưu trữ trong cơ sở dữ liệu.

+1

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

Trả lời

186

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

Để làm đ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ữ mức sử dụng bộ nhớ 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 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'); 
} 

Nhanh chóng tổng quan của ưu và nhược điểm của phương pháp tiếp cận khác nhau:

XMLReader chỉ

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

  • Nhược điểm: quá khó viết và gỡ lỗi, đòi hỏi rất nhiều mã userland để 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ã để 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: việc tạo đối tượng SimpleXMLElement cho mỗi nút không phải là rất nhanh. Bạn thực sự phải đánh giá nó để hiểu xem đó là một vấn đề cho bạn. Ngay cả một máy khiêm tốn sẽ có thể xử lý một nghìn nút mỗi giây, mặc dù.

XMLReader + DOM

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

  • Nhược điểm: DOM gây phiền toái khi làm việc. Nó nằm giữa XMLReader và SimpleXML.Không phức tạp và lúng túng như XMLReader, nhưng năm ánh sáng không hoạt động với SimpleXML.

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

+0

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

+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. Điểm bất lợi là nó sẽ mất nhiều thời gian hơn để viết và khó khăn hơn nhiều để gỡ lỗi. –

+1

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

2

XMLReader được ghi lại trên PHP site. Đâ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 các nút DOM) của tài liệu XML đã cho. Ví dụ, bạn có thể đi qua toàn bộ tài liệu bạn đã như thế này:

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

Đó là sau đó tùy thuộc vào bạn để quyết định làm thế nào để đối phó với các nút được trả về bởi XMLReader::expand().

+0

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

+20

Cũng liên quan đến XMLReader cũng được ghi lại trên php.net tôi sẽ không đồng ý, đó là một trong những chức năng được ghi nhận tồi tệ nhất mà tôi đã 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 quyết định trước khi yêu cầu ở đây :) –

+0

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 xét nó. –

8

Đố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(); 
+0

Mặc dù mã có nhiều chi tiết và cách thủ công hơn để đi qua XML, điều này sẽ tiết kiệm được sự tỉnh táo của bạn, vì DOMDocument và SimpleXML có xu hướng giữ cho bạn đoán được những gì sẽ được trả lại. – b01

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

Câu trả lời được chấp nhận cho tôi một khởi đầu tốt, nhưng mang lại nhiều lớp học và xử lý nhiều hơn tôi đã có thể thích; 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(); 
4

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

Tôi tìm cách dễ nhất để sử dụng XMLReader là biết tôi muốn thông tin nào ra 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. Các trường hợp 2 và 3 chỉ để cho bạn biết làm thế nào nó có thể được thực hiện cho các thẻ phức tạp hơn. Điều này cực kỳ nhanh; Tôi có một cuộc thảo luận về tốc độ so với trên What is the fastest XML parser in PHP?

Điều quan trọng nhất cần nhớ khi thực hiện phân tích từ khóa dựa trên như thế này là sử dụng if ($myXML->nodeType == XMLReader::ELEMENT) {... - đó kiểm tra để chắc chắn rằng chúng tôi chỉ đối phó với các nút mở và không khoảng trắng hoặc đóng cửa nút 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(); 
} 
0

Tôi e rằng việc sử dụng XmlReader :: expand() có thể tiêu thụ khá nhiều RAM khi subtree không quá nhỏ. Tôi không chắc đó là một lựa chọn tốt cho XmlReader. Tuy nhiên, tôi đồng ý rằng XmlReader thực sự yếu và không thích hợp để xử lý các cây XML lồng nhau phức tạp. Tôi thực sự không thích hai thứ: đầu tiên, nút hiện tại không có đường dẫn trong cây XML có thể truy cập dưới dạng thuộc tính, thứ hai là không thể chạy xử lý giống XPath trong khi đọc các nút. Tất nhiên truy vấn XPath thực sẽ tốn rất nhiều thời gian cho XML lớn, nhưng "đường dẫn móc" có thể được sử dụng thay thế - chẳng hạn như khi đường dẫn phần tử hiện tại khớp với cây con (root) một hàm/phương thức PHP cháy. Vì vậy, tôi đã phát triển các lớp học của riêng tôi trên đầu trang của XmlReader một vài năm trước đây. Họ không phải là hoàn hảo và có lẽ tôi muốn viết tốt hơn hiện nay tuy nhiên vẫn có thể có ích cho một ai đó:

https://bitbucket.org/sdvpartnership/questpc-framework/src/c481a8b051dbba0a6644ab8a77a71e58119e7441/includes/Xml/Reader/?at=master

tôi xây dựng con đường XML 'node1/node2' sau đó bản thân mình sử dụng móc với PCRE phù hợp mà ít mạnh mẽ hơn XPath, tuy nhiên là đủ cho nhu cầu của tôi. Tôi đã xử lý XML khá phức tạp với các lớp này.

1

Chủ đề này được thoát ra từ lâu, nhưng tôi chỉ tìm thấy nó. Cảm ơn Chúa.

Vấn đề của tôi là tôi phải đọc tệp ONIX (dữ liệu sách) và lưu trữ nó vào cơ sở dữ liệu của chúng tôi. Tôi sử dụng simplexml_load trước đây, và mặc dù nó sử dụng rất nhiều bộ nhớ nhưng vẫn ok cho tập tin tương đối nhỏ (lên đến 300MB). Ngoài kích thước đó là một thảm họa đối với tôi.

Sau khi đọc, đặc biệt là diễn giải của Francis Lewis, tôi sử dụng kết hợp xmlreader và simplexml. Kết quả là đặc biệt, sử dụng bộ nhớ nhỏ và chèn nó vào cơ sở dữ liệu đủ nhanh cho tôi.

Đây là mã của tôi:

<?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; 
?> 
+0

Điều này không thực sự trả lời câu hỏi - nó dường như chỉ là một ví dụ riêng biệt về cách bạn có thể giải quyết một vấn đề khác. –

Các vấn đề liên quan