首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >XML解析PHP

XML解析PHP
EN

Stack Overflow用户
提问于 2018-06-12 23:21:29
回答 2查看 68关注 0票数 0

我需要解析这个具有一些自定义标记的XML文件,如下所示:

代码语言:javascript
复制
    <?xml version="1.0" encoding="utf-8"?>
<glz:Config xmlns:glz="http://www.glizy.org/dtd/1.0/">
    <glz:Import src="config.xml" />

    <glz:Group name="thumbnail">
        <glz:Param name="width" value="200" />
        <glz:Param name="height" value="*" />
    </glz:Group>
</glz:Config>

当它到达标记<glz:Import src="config.xml" />时,它需要解析文件config.xml,该文件包含以下内容:

代码语言:javascript
复制
    <?xml version="1.0" encoding="utf-8"?>
<glz:Config xmlns:glz="http://www.glizy.org/dtd/1.0/">
    <glz:Group name="folder">
        <glz:Param name="width" value="100" />
        <glz:Param name="height" value="200" />
    </glz:Group>
</glz:Config>

最终结果应该是如下所示的数组。它包含两个已解析文件的值:

代码语言:javascript
复制
$result['thumbnail/width'] = 200;
$result['thumbnail/height'] = '*';
$result['folder/width'] = 100;
$result['folder/height'] = 200;

这就是我管理XML解析的方式。我的问题是,我不知道如何将新的结果与已经(旧)解析的结果合并。你可以在这里看到我的代码:

代码语言:javascript
复制
function parseFile(){
            $reader = new XMLReader;
            $reader->open($this->fileName);

            while ($reader->read()){
                if ($reader->name == 'glz:Group')
                {
                    $groupName = $reader->getAttribute('name');
                    $reader->read();
                    $reader->read();

                    while ($reader->name == 'glz:Param')
                    {
                        if (strpos($reader->getAttribute('name'),'[]')  == true)
                        {
                            $arrayGroupName = substr($reader->getAttribute('name'), 0, -2);
                            if(empty($filters[$groupName.'/'.$arrayGroupName]))
                            {
                                $filters[$groupName.'/'.$arrayGroupName] = array();
                                array_push($filters[$groupName.'/'.$arrayGroupName],$this->castValue($reader->getAttribute('value')));
                                $this->result[$groupName."/".$arrayGroupName] = $filters[$groupName.'/'.$arrayGroupName];
                            }
                            else
                            {
                                array_push($filters[$groupName.'/'.$arrayGroupName],$this->castValue($reader->getAttribute('value')));
                                $this->result[$groupName."/".$arrayGroupName] = $filters[$groupName.'/'.$arrayGroupName];
                            }
                        }
                        else
                        {
                            $this->result[$groupName."/".$reader->getAttribute('name')] = $this->castValue($reader->getAttribute('value'));
                        }
                        $reader->read();
                        $reader->read();
                    }
                }
                else if ($reader->name == 'glz:Param')
                {
                    if (strpos($reader->getAttribute('name'),'[]')  == true)
                    {
                        $arrayGroupName = substr($reader->getAttribute('name'), 0, -2);
                        if(empty($filters[$arrayGroupName]))
                        {
                            $filters[$arrayGroupName] = array();
                            array_push($filters[$arrayGroupName],$this->castValue($reader->getAttribute('value')));
                            $this->result[$$arrayGroupName] = $filters[$arrayGroupName];
                        }
                        else
                        {
                            array_push($filters[$arrayGroupName],$this->castValue($reader->getAttribute('value')));
                            $this->result[$arrayGroupName] = $filters[$arrayGroupName];
                        }
                    }
                    else
                    {
                        $this->result[$reader->getAttribute('name')] = $this->castValue($reader->getAttribute('value'));
                    }
                }
                else if ($reader->name == 'glz:Import')
                {
                    $file = $reader->getAttribute('src');
                    $newConfig = new Config($file);
                    $newConfig->parseFile();
                }
            }
            return $this->result;

        }

如何在每次找到标记时合并解析文件得到的结果?

非常感谢!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-06-13 00:51:38

据我所知,你需要对你的代码进行一些重构。

重写解析器函数,不引用$this->result和$this->fileName。

在函数中将这些变量重新声明为$result和$fileName。添加$fileName作为函数参数。

在函数中添加另一个变量$result_config。

读取config标记时,递归调用函数,而不是创建新类:

代码语言:javascript
复制
 -$file = $reader->getAttribute('src');
 - $newConfig = new Config();

 + $file = $reader->getAttribute('src');
 + $result_config = $this->parseFile($file);

最后,在处理完这两个文件后,将两个结果合并:

代码语言:javascript
复制
if ($result_config) {
    $this->result = array_merge($result_config, $this->result);
}
return $this->result;
票数 1
EN

Stack Overflow用户

发布于 2018-06-13 02:50:28

您需要将读取逻辑放入一个以文件名为参数的函数中,以便它可以在找到Import元素时调用自身。让函数以数组的形式返回值并合并结果。

在DOM中,这并不复杂:

代码语言:javascript
复制
function readConfigurationFile($fileName) {
  $document = new DOMDocument();
  $document->load($fileName);
  $xpath = new DOMXpath($document);
  $xpath->registerNamespace('g', 'http://www.glizy.org/dtd/1.0/');

  $result = [];
  foreach ($xpath->evaluate('/g:Config/*[self::g:Import or self::g:Group]') as $node) {
    switch ($node->localName) {
    case 'Import' :
      $result = array_merge($result, readConfigurationfile($node->getAttribute('src')));
      break;
    case 'Group' :
      $groupName = $node->getAttribute('name'); 
      foreach ($xpath->evaluate('g:Param', $node) as $paramNode) {
        $result[
          sprintf('%s/%s', $groupName, $paramNode->getAttribute('name'))
        ] = $paramNode->getAttribute('value');
      } 
      break;
    }
  }
  return $result;
}

var_dump(readConfigurationFile('main.xml'));

输出:

代码语言:javascript
复制
array(4) {
  ["folder/width"]=>
  string(3) "100"
  ["folder/height"]=>
  string(3) "200"
  ["thumbnail/width"]=>
  string(3) "200"
  ["thumbnail/height"]=>
  string(1) "*"
}

该方法在XMLReader中是相同的,但稍微复杂一些。

代码语言:javascript
复制
function readLargeConfigurationFile($fileName) {

  $reader = new XMLReader();
  $reader->open($fileName);

  $xmlns = 'http://www.glizy.org/dtd/1.0/';
  $document = new DOMDocument();
  $xpath = new DOMXpath($document);
  $xpath->registerNamespace('g', $xmlns);

  $result = [];

  // find the first Import or Group in the namespace
  do {
    $found = $reader->read();
  } while(
    $found && 
    !(
       $reader->namespaceURI === $xmlns && 
       ($reader->localName === 'Import' || $reader->localName === 'Group')
    )
  );

  while ($found) {
    switch ($reader->localName) {
    case 'Import' :
      $result = array_merge($result, readLargeConfigurationFile($reader->getAttribute('src')));
      break;
    case 'Group' :
      // expand Group into DOM for easier access
      $groupNode = $reader->expand($document);
      $groupName = $groupNode->getAttribute('name'); 
      foreach ($xpath->evaluate('g:Param', $groupNode) as $paramNode) {
        // read a Param
        $result[
          sprintf('%s/%s', $groupName, $paramNode->getAttribute('name'))
        ] = $paramNode->getAttribute('value');
      } 
      break;
    }

    // iterate sibling nodes to find the next Import or Group
    do {
      $found = $reader->next();
    } while(
      $found && 
      !(
        $reader->namespaceURI === $xmlns && 
        ($reader->localName === 'Import' || $reader->localName === 'Group')
      )
    ); 
  } 
  return $result;
}

var_dump(readLargeConfigurationFile('main.xml'));

请注意,该示例没有使用$name属性。它包含名称空间别名/前缀glz。名称空间前缀是可选的,可以更改-即使在单个文档中也是如此。使用$localName$namespaceURI属性。

使用XMLReader::expand(),您可以将当前节点展开为DOM。一种典型的方法是使用XML阅读器仅迭代外部节点。如果您知道一个节点及其后代足够小,那么可以将它们扩展到DOM中以便于访问。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50820655

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档