Конечный элемент XML читается дважды с использованием XMLReader с PHP - PullRequest
1 голос
/ 21 февраля 2011

Я хочу прочитать файл XML, используя XMLReader, но элемент END ELEMENT дважды вызывается для каждого элемента во время синтаксического анализа.

<publications>
  <article id="Xu86oazdn">
    <title>Learning</title>
    <authors>
      <author>
        <firstname>Michel</firstname>
        <lastname>Browsky</lastname>
      </author>
    </authors>
  </article>
</publications>

Это фрагмент кода, который анализирует записи автора:

<?php
$xml = new XMLReader();
$xml->open("php://stdin");
$author = null;

while($xml->read()) {

  switch($xml->nodeType) {
    case XMLReader::ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("+" . $xml->name);
          break;
    }

    case XMLReader::END_ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("-" . $xml->name);
          break;
      }
    }
  }
?>

Но, как ни странно, END_ELEMENT вызывается дважды для каждого </author>, как показано в эхо-сообщениях:

+author
-author
-author

Если я заменю эхо-сообщение вызовом $xml->readOuterXML(),первое END_ELEMENT выглядит следующим образом:

<author>
  <firstname>Michel</firstname>
  <lastname>Browsky</lastname>
</author>

А второе следующее:

<author/>

Что не так с моим кодом?Я неправильно использовал END_ELEMENT?Как правильно определить конечный элемент?

1 Ответ

8 голосов
/ 21 февраля 2011

Добавьте оператор break после окончания первого условия switch в nodeType:

<?php
$xml = new XMLReader();
$xml->open("php://stdin");

while($xml->read()) {

  switch($xml->nodeType) {
    case XMLReader::ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("+" . $xml->name);
          break;
    }

    // THIS LINE IS MISSING
    break;

    case XMLReader::END_ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("-" . $xml->name);
          break;
      }
    }
  }
?>

Добавьте еще один break после прочтения END_ELEMENT, еслитолько для симметрии.

    case XMLReader::END_ELEMENT:
      switch($xml->name) {
        case 'author':
          echo("-" . $xml->name);
          break;
      }
    }

    break;

Проблема возникла из-за стиля кодирования.Упростите код.Например:

$xml = new XMLReader();
$xml->open("php://stdin");

while($xml->read()) {    
  switch($xml->nodeType) {
    case XMLReader::ELEMENT: {
      startElement( $xml->name );
      break;
    }

    case XMLReader::END_ELEMENT: {
      endElement( $xml->name );
      break;
    }
  }
}

Существуют и другие упрощения.В PHP есть пакет сортировки XML, но вы также можете абстрагировать код в классы.Экземпляры этих классов смогут затем читать (или записывать) себя из (или в) XML-файла.Например:

$xml = new XMLReader();
$xml->open("php://stdin");

while($xml->read()) {    
  if( $xml->name == 'author' ) {
    $author = new Author();
    $author->marshall( $xml );
  }
}

Это объединяет детали того, как объект хранится с самим объектом.Каждый раз, когда вы меняете объект Author, вы знаете, что должны изменить способ его маршалинга.Вы можете абстрагировать и расширить эти концепции еще больше, используя соответствующие шаблоны проектирования, XML-схемы и т. Д.

Таким образом, ваш окончательный код может выглядеть следующим образом:

$xml = new XMLReader();
$xml->open( "php://stdin" );
$publications = new Publications();
$publications->marshall( $xml );

За объект Publications отвечаетдля чтения XML-документа и создания экземпляров соответствующих классов всякий раз, когда появляются связанные с ними XML-теги:

while($xml->read()) {    
  $article = new Article();
  $article->marshall( $xml );
  add( $article );
}

Используйте PHP-фреймворк для маршаллинга, чтобы сэкономить время и силы.Рассмотрим XML_Serializer:

...