使用带有PHP的XMLReader读取XML结束元素两次

时间:2011-02-20 23:42:02

标签: php xml xmlreader

我想使用XMLReader读取XML文件,但在解析过程中,每个元素都会调用两次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;
      }
    }
  }
?>

但奇怪的是,每个</author>都会调用两次END_ELEMENT,如回显消息所示:

+author
-author
-author

如果我通过调用$xml->readOuterXML()替换回显消息,则第一个END_ELEMENT如下:

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

第二个是以下内容:

<author/>

我的代码出了什么问题?我是否以错误的方式使用END_ELEMENT?检测结束元素的正确方法是什么?

1 个答案:

答案 0 :(得分:8)

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: