在PHP中是否有一种优雅的方法来处理具有深层嵌套的可选节点的JSON?

时间:2017-03-15 23:48:22

标签: php json

我正在使用Oxford Dictionary API来查找单词定义。返回的JSON可能是13级或更高级别。它是对象的数组,我永远无法确定任何一个对象都会存在。

我想返回一个简单的定义数组,存储在$list

所以我发现自己写了这个恐怖故事:

$list = [];
$data = json_decode( $response->getBody() );

//oh gross
if( isset( $data->results ) ) {
    foreach( $data->results as $r ) {
        if( isset( $r->lexicalEntries ) ) {
            foreach( $r->lexicalEntries as $l ) {
                if( isset( $l->entries ) ) {
                    foreach( $l->entries as $e ) {
                        if( isset( $e->senses ) ) {
                            foreach( $e->senses as $s ) {
                                if( isset( $s->definitions ) ) {
                                    foreach( $s->definitions as $d ) {
                                        $list[] = [
                                            'word' => $r->word,
                                            'definition' => $d
                                        ];
                                    }
                                }

                                if( isset( $s->subsenses ) ) {
                                    foreach( $s->subsenses as $ss ) {
                                        if( isset( $ss->definitions ) ) {
                                            foreach( $ss->definitions as $d ) {
                                                $list[] = [
                                                    'word' => $r->word,
                                                    'definition' => $d
                                                ];
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}

return $list;

哪个有效,但只是完全讨厌。

我考虑过递归函数,但不确定它是否更具可读性。

有更优雅的方式吗?

1 个答案:

答案 0 :(得分:2)

嗯,我必须说实话,因为你的方法的结果只是一个包含两个索引的数组,其中word总是相同的而definition是不同的。如果你真的希望$r->worddefinition一起存储,那么几乎无法做到。

但是,您可以使用以下代码缩短isset()代码

  1. tanery operator ,PHP版本< 7
  2. null coalesce operator with PHP> = 7。
  3. 两个版本的例子:

    // PHP < 7.
    foreach(isset($data['results']) ? $data['results'] : [] as $value){}
    
    // PHP > 7
    foreach($data['results'] ?? [] as $value){}
    

    但是,这只是为了抑制错误报告,您可以暂时将其关闭并重新打开,只是假设索引存在并忘记isset()

    我已经定义了这样的数据:

    $json = file_get_contents('data.json');
    $data = json_decode($json, true);
    

    在这种情况下,我更喜欢对象的数组,以及使用PHP 7 null coalesce运算符处理方法的副本:

    foreach($data['results'] ?? [] as $r){
      foreach($r['lexicalEntries'] ?? [] as $l){
        foreach($l['entries'] ?? [] as $e){
          foreach($e['senses'] ?? [] as $s){
            foreach( $s['definitions'] ?? [] as $d){
              $list[] = [
                'word' => $r['word'],
                'definition' => $d
              ];
            }
    
            foreach($s['subsenses'] ?? [] as $ss){
              foreach( $ss['definitions'] ?? [] as $d){
                $list[] = [
                  'word' => $r['word'],
                  'definition' => $d
                ];
              }
            }
          }
        }
      }
    }
    
    print_r($list);
    

    但是,还有其他方法可以做到这一点,那就是展平数组。有多种方法可以做到这一点,我更喜欢生成器,但在PHP 7及更高版本中引入了递归生成器。

    // This is a generator function, it flattens the entire given array.
    // It yields an array containing the key and value on each iteration.
    function array_walk_generator($array){
      foreach($array as $k => $v){
        yield $k => $v;
    
        if(is_array($v)){
          yield from array_walk_generator($v);
        }
      }
    }
    
    foreach($data['results'] as $result){
      $list = [];
    
      foreach(array_walk_generator($result) as $k => $v){
        if($k == 'definitions' && is_array($v)){
          $list[] = $v[0];
        }
      }
    
      $return[] = [
        'word' => $result['word'],
        'definitions' => array_values(array_filter($list))
      ];
    }
    
    print_r($return);
    

    这样你就可以看到一个更有组织的数组,这种方式并不关心定义的格式。