PHP에서 XMLReader를 사용하는 방법?


79

다음 XML 파일이 있고 파일이 다소 크며 파일을 열고 읽을 수 있도록 simplexml을 얻을 수 없었으므로 PHP에서 성공하지 못한 채 XMLReader를 시도하고 있습니다.

<?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에 대한 좋은 자습서를 찾지 못했고 각 요소 콘텐츠를 데이터베이스에 저장할 수있는 방법을보고 싶습니다.


1
PHP 문서에서 사용자 제공 예제를 읽었습니까? php.net/manual/en/class.xmlreader.php#61929 가 도움이 될 수 있습니다.
mcrumley 2009

답변:


225

그것은 모두 작업 단위의 크기에 달려 있지만 각 <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 코드는 느리고 오류가 발생하기 쉽습니다. 또한 유지 관리 할 코드 줄이 더 많이 남습니다.

XMLReader + SimpleXML

  • 장점 : 많은 메모리를 사용하지 않으며 (단 하나의 노드를 처리하는 데 필요한 메모리 만) SimpleXML은 이름에서 알 수 있듯이 사용하기가 정말 쉽습니다.

  • 단점 : 각 노드에 대해 SimpleXMLElement 개체를 만드는 것은 그리 빠르지 않습니다. 그것이 당신에게 문제인지 이해하기 위해 정말로 그것을 벤치마킹해야합니다. 하지만 아무리 평범한 기계라도 초당 1000 개의 노드를 처리 할 수 ​​있습니다.

XMLReader + DOM

  • 장점 : SimpleXML만큼 많은 메모리를 사용하며 XMLReader :: expand () 는 새 SimpleXMLElement를 만드는 것보다 빠릅니다. 사용이 가능했으면 좋겠 simplexml_import_dom()는데 그 경우에는 안되는 것 같아요

  • 단점 : DOM은 작업하기가 귀찮습니다. XMLReader와 SimpleXML의 중간입니다. XMLReader만큼 복잡하고 어색하지는 않지만 SimpleXML로 작업하는 데 수년이 걸립니다.

나의 조언 : SimpleXML로 프로토 타입을 작성하고 그것이 당신에게 맞는지 확인하라. 성능이 가장 중요하다면 DOM을 사용해보십시오. 가능한 한 XMLReader에서 멀리 떨어져 있습니다. 더 많은 코드를 작성할수록 버그가 발생하거나 성능 회귀가 발생할 가능성이 높아집니다.


1
순수하게 XMLReader로 이것을 수행하는 방법이 있습니까? 아니면 이점이 없습니까?
Shadi Almosri 2009

2
XMLReader로 전적으로 할 수 있습니다. 장점은 더 빠르며 약간의 메모리를 사용한다는 것입니다. 단점은 작성하는 데 상당히 오래 걸리고 디버그하기가 훨씬 더 어렵다는 것입니다.
Josh Davis

2
첫 번째 제품 노드로 이동할 때 $ z-> next ( 'product')를 사용하지 않은 이유는 무엇입니까?
연상

특정 코드가 기억 나지 않습니다. 죄송합니다. 메모를 추가하지 않았다면 가능성을 간과했을 수도 있습니다.
조쉬 데이비스

1
대부분의 XMLReader 기반 구문 분석은 반복기 패턴으로 표현 / 래핑 할 수 있습니다. 이를 위해 몇 가지 유용한 반복기와 필터를 컴파일했습니다. git.io/xmlreaderiterator ( gist )
hakre

10

속성으로 형식화 된 xml의 경우 ...

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>

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
코드가 훨씬 더 장황하고 수동으로 XML을 살펴볼 수 있지만, DOMDocument와 SimpleXML이 반환 될 내용을 계속 추측하는 경향이 있기 때문에이 방법을 사용하면 온전한 상태를 유지할 수 있습니다.
b01

6

받아 들여진 대답은 나에게 좋은 출발을 주었지만 내가 원했던 것보다 더 많은 수업과 더 많은 처리를 가져왔다. 그래서 이것은 내 해석입니다.

$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

XML 파싱 작업의 대부분은 XML (Amazon MWS) 트럭에서 유용한 정보를 추출하는 데 소비됩니다. 따라서 내 대답은 특정 정보 만 원하고 위치를 알고 있다고 가정합니다.

XMLReader를 사용하는 가장 쉬운 방법은 정보를 원하는 태그를 알고 사용하는 것입니다. XML의 구조를 알고 있고 고유 한 태그가 많으면 첫 번째 경우를 사용하는 것이 쉽습니다. 사례 2와 3은 더 복잡한 태그에 대해 어떻게 할 수 있는지 보여주기위한 것입니다. 이것은 매우 빠릅니다. PHP에서 가장 빠른 XML 파서는 무엇입니까? 에 대해 속도에 대해 논의 했습니다.

이와 같이 태그 기반 구문 분석을 수행 할 때 기억해야 할 가장 중요한 것은 사용 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();
}

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는 PHP 사이트 에 잘 문서화되어 있습니다 . 이것은 XML 풀 파서로, 주어진 XML 문서의 노드 (또는 DOM 노드)를 반복하는 데 사용됩니다. 예를 들어 다음과 같이 제공 한 전체 문서를 살펴볼 수 있습니다.

<?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 ()에 의해 반환 된 노드를 처리하는 방법을 결정하는 것은 사용자에게 달려 있습니다.


처리가 완료된 후 다음 노드로 이동하려면 어떻게해야합니까?
Shadi Almosri 2009

24
또한 XMLReader가 php.net에 잘 문서화되어 있다는 점에 대해서는 동의하지 않을 것입니다. 이것은 제가 본 최악의 문서화 기능 중 하나이며 php.net을 오랫동안 사용해 왔으며 이전에이 문제를 해결하기 위해 향한 첫 번째 장소였습니다. 여기에 질문 :)
Shadi Almosri 2009

XMLReader :: read ()가 한 노드에서 다른 노드로 이동하는 방식을 이해하고 있는지 잘 모르겠습니다. XMLReader 클래스는 또한 PHP에서도 사용할 수있는 잘 알려진 라이브러리 인 libxml을 사용합니다.
Percutio 2009

12
XMLReader가 잘 문서화되어 있다는 생각은 말도 안됩니다. 문제는 어디서부터 시작해야할지 모르면 아무데도 알려주지 않는다는 것입니다. 어떤 것을 호출해야할지 알지 못한다면 클래스 메서드의 세탁 목록을 제공하는 것은 쓸모가 없습니다.
xgretsch 2011 년

0

이것은 나를 위해 더 좋고 더 빠르게 작동합니다.


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

** 백엔드 파일 **


<?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>");
}
?> 

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.