PHP - 递归数组到对象?

Posted

技术标签:

【中文标题】PHP - 递归数组到对象?【英文标题】:PHP - recursive Array to Object? 【发布时间】:2011-06-14 23:55:18 【问题描述】:

有没有办法在 php 中将多维 array 转换为 stdClass 对象?

转换为(object) 似乎不能递归工作。 json_decode(json_encode($array)) 产生了我正在寻找的结果,但必须有更好的方法......

【问题讨论】:

【参考方案1】:

据我所知,没有预先构建的解决方案,所以你可以自己动手:

function array_to_object($array) 
  $obj = new stdClass;
  foreach($array as $k => $v) 
     if(strlen($k)) 
        if(is_array($v)) 
           $obj->$k = array_to_object($v); //RECURSION
         else 
           $obj->$k = $v;
        
     
  
  return $obj;
 

【讨论】:

PHP 数组可以有空键,但对象不能有空属性。可能值得将 else 更改为 elseif ($k !== '') 感谢您注意到仅使用 elseif 是无法完成的! 这个函数似乎会默默地删除具有空键的值。 嵌套数组有数字键会发生什么?我会选择 json 编码/解码解决方案。它可能不是最佳的,但却是最防弹的。 json编码/解码方法会将非StdClass对象转换为StdClass对象。【参考方案2】:

我知道这个答案来晚了,但我会把它发布给任何正在寻找解决方案的人。

您可以使用 PHP 的原生 json_* 函数来代替所有这些循环等。我有几个我经常使用的方便的功能

/**
 * Convert an array into a stdClass()
 * 
 * @param   array   $array  The array we want to convert
 * 
 * @return  object
 */
function arrayToObject($array)

    // First we convert the array to a json string
    $json = json_encode($array);

    // The we convert the json string to a stdClass()
    $object = json_decode($json);

    return $object;



/**
 * Convert a object to an array
 * 
 * @param   object  $object The object we want to convert
 * 
 * @return  array
 */
function objectToArray($object)

    // First we convert the object into a json string
    $json = json_encode($object);

    // Then we convert the json string to an array
    $array = json_decode($json, true);

    return $array;

希望对你有帮助

【讨论】:

我很欣赏这个迟到总比从来没有好过的帖子,但我最初的问题引用了这种方法。 对于我是如何设法忽略这一点的,我没有任何有效的辩护:/ 我唯一能做的就是戴上眼镜并引用史蒂夫·厄克尔的话;我这样做了吗!?回到主题:据我所知,以我的拙见,这是最干净、最优化的方法。如果我错了,请纠正我:) 如果你从代码行来判断,它可能是最干净的,但涉及的开销要多得多。 Jacob Relkin 的答案更高效,也更容易编码。 这仅适用于兼容的 JSON 数据类型。如果你像我一样在多维数组中存储闭包,这将不起作用。【参考方案3】:

您和许多其他人都指出了 JSON 内置函数 json_decode()json_encode()。您提到的方法有效,但并不完全:它不会将索引数组转换为对象,它们将保留为索引数组。但是,有一个技巧可以克服这个问题。你可以使用JSON_FORCE_OBJECT常量:

// Converts an array to an object recursively
$object = json_decode(json_encode($array, JSON_FORCE_OBJECT));

提示:另外,如 here 所述,您可以使用 JSON 函数递归地将对象转换为数组:

// Converts an object to an array recursively
$array = json_decode(json_encode($object), true));    

重要提示:如果您确实关心性能,请不要使用此方法。虽然它短而干净,但它是替代品中最慢的。请参阅此线程中的my other answer。

【讨论】:

【参考方案4】:
function toObject($array) 
    $obj = new stdClass();
    foreach ($array as $key => $val) 
        $obj->$key = is_array($val) ? toObject($val) : $val;
    
    return $obj;

【讨论】:

这将为数组中的空键生成致命错误(无法访问空属性)。观察strlen in Jacob's answer【参考方案5】:

您可以递归地使用array_map

public static function _arrayToObject($array) 
    return is_array($array) ? (object) array_map([__CLASS__, __METHOD__], $array) : $array;

非常适合我,因为它不会将例如 Carbon 对象转换为基本的 stdClass(json 编码/解码会这样做)

【讨论】:

当你在一个类中需要这种类型的实用程序时,这很简单,也很实用;很难找到其中的参考资料。为了清楚起见,您应该将其包装在示例类中。否则,CLASSMETHOD 魔法的好例子......你不会经常看到它。【参考方案6】:
/**
 * Recursively converts associative arrays to stdClass while keeping integer keys subarrays as arrays
 * (lists of scalar values or collection of objects).
 */
function a2o( array $array ) 
    $resultObj = new \stdClass;
    $resultArr = array();
    $hasIntKeys = false;
    $hasStrKeys = false;
    foreach ( $array as $k => $v ) 
        if ( !$hasIntKeys ) 
            $hasIntKeys = is_int( $k );
        
        if ( !$hasStrKeys ) 
            $hasStrKeys = is_string( $k );
        
        if ( $hasIntKeys && $hasStrKeys ) 
            $e = new \Exception( 'Current level has both integer and string keys, thus it is impossible to keep array or convert to object' );
            $e->vars = array( 'level' => $array );
            throw $e;
        
        if ( $hasStrKeys ) 
            $resultObj->$k = is_array( $v ) ? a2o( $v ) : $v;
         else 
            $resultArr[$k] = is_array( $v ) ? a2o( $v ) : $v;
        
    
    return ($hasStrKeys) ? $resultObj : $resultArr;
 

【讨论】:

【参考方案7】:

此处发布的其他一些解决方案无法区分顺序数组(JS 中的 [])和映射(JS 中的 )。对于许多用例,区分包含所有内容的 PHP 数组很重要从没有数字键的 PHP 数组中应该保留的顺序数字键,应该将其转换为对象。 (对于不属于上述两类的数组,我下面的解决方案是未定义的。)

json_decode(json_encode($x)) 方法确实可以正确处理这两种类型,但不是最快的解决方案。尽管如此,它仍然很不错,在我的样本数据上每次运行总计 25µs(平均超过 1M 次运行,减去循环开销。)

我对递归转换器的几个变体进行了基准测试,最终得到以下结果。它重建所有数组和对象(执行深拷贝),但似乎比修改数组的替代解决方案更快。每次执行我的样本数据时,它的时钟为 11µs

function array_to_object($x) 
    if (!is_array($x)) 
        return $x;
     elseif (is_numeric(key($x))) 
        return array_map(__FUNCTION__, $x);
     else 
        return (object) array_map(__FUNCTION__, $x);
    

这是一个就地版本。在一些只需要转换小部分的大型输入数据上可能会更快,但在我的示例数据上,每次执行需要 15µs

function array_to_object_inplace(&$x) 
    if (!is_array($x)) 
        return;
    
    array_walk($x, __FUNCTION__);
    reset($x);
    if (!is_numeric(key($x))) 
        $x = (object) $x;
    

我没有尝试使用 array_walk_recursive() 的解决方案

【讨论】:

第一种方法,只是创建了一个空对象。对我来说,第二种方法只能将最外层的关联数组变成stdClass 对象。其他所有内容在内部仍然是一个数组。 @KimberlyW 我刚刚测试了两者,它们仍然在 PHP 7 中工作。See this example【参考方案8】:
public static function _arrayToObject($array) 
    $json = json_encode($array);
    $object = json_decode($json);
    return $object

【讨论】:

这与this answer有何不同? @MAChitgarha 那里给出的答案使用循环机制。但这里的解决方案是使用比提到的答案更有效的内置函数 不,我指的不是公认的答案,而是 Ole 的。它说的和你的完全一样,但大约是 6 年前给出的。【参考方案9】:

将关联数组转换为对象的最简单方法是:

先用json编码,再解码。

点赞$objectArray = json_decode(json_encode($associtiveArray));

【讨论】:

抱歉,您的回答对this answer没有任何帮助。【参考方案10】:

因为提到了性能,实际上很多地方应该很重要,所以我尝试对这里回答的函数进行基准测试。

您可以看到代码和示例数据here in this gist。结果是用那里存在的数据进行测试的(一个随机的 JSON 文件,大小约为 200 KB),每个函数重复一千次,以使结果更准确。

以下是不同 PHP 配置的结果:

PHP 7.4.16(无 JIT)

$ php -dopcache.enable_cli=1 benchmark.php
pureRecursive():                        Completed in 0.000560s
pureRecursivePreservingIntKeys():       Completed in 0.000580s
jsonEncode():                           Completed in 0.002045s
jsonEncodeOptimized():                  Completed in 0.002060s
jsonEncodeForceObject():                Completed in 0.002174s
arrayMap():                             Completed in 0.000561s
arrayMapPreservingIntKeys():            Completed in 0.000592s
arrayWalkInplaceWrapper():              Completed in 0.001016s

PHP 8.0.2(无 JIT)

$ php -dopcache.enable_cli=1 benchmark.php
pureRecursive():                        Completed in 0.000535s
pureRecursivePreservingIntKeys():       Completed in 0.000578s
jsonEncode():                           Completed in 0.001991s
jsonEncodeOptimized():                  Completed in 0.001990s
jsonEncodeForceObject():                Completed in 0.002164s
arrayMap():                             Completed in 0.000579s
arrayMapPreservingIntKeys():            Completed in 0.000615s
arrayWalkInplaceWrapper():              Completed in 0.001040s

PHP 8.0.2(跟踪 JIT)

$ php -dopcache.enable_cli=1 -dopcache.jit_buffer_size=250M -dopcache.jit=tracing benchmark.php
pureRecursive():                        Completed in 0.000422s
pureRecursivePreservingIntKeys():       Completed in 0.000410s
jsonEncode():                           Completed in 0.002004s
jsonEncodeOptimized():                  Completed in 0.001997s
jsonEncodeForceObject():                Completed in 0.002094s
arrayMap():                             Completed in 0.000577s
arrayMapPreservingIntKeys():            Completed in 0.000593s
arrayWalkInplaceWrapper():              Completed in 0.001012s

如您所见,使用此基准测试最快的方法是纯递归 PHP 函数(由 @JacobRelkin 和 @DmitriySintsov 发布),尤其是在涉及 JIT 编译器时。对于json_* 函数,它们是最慢的。它们比纯方法慢了大约 3 到 4 倍(在 JIT 的情况下为 5 倍),这似乎令人难以置信。

需要注意的一点:如果您删除迭代(即每个函数只运行一次),或者甚至严格降低其计数,结果会有所不同。在这种情况下,arrayMap*() 变体胜过pureRecursive*() 变体(仍然json_* 函数方法应该是最慢的)。但是,您应该简单地忽略这些情况。在性能方面,可扩展性更为重要。

因此,在将数组转换为对象(反之亦然?)的情况下,您应该始终使用纯 PHP 函数,从而获得最佳性能,这可能与您的配置无关。

【讨论】:

【参考方案11】:

这是一个使用 PHP 内部(浅)数组到对象类型转换机制进行就地深度数组到对象转换的函数。 它仅在必要时创建新对象,从而最大限度地减少数据重复。

function toObject($array) 
    foreach ($array as $key=>$value)
        if (is_array($value))
            $array[$key] = toObject($value);
    return (object)$array;

警告 - 如果存在循环引用的风险,请勿使用此代码。

【讨论】:

【参考方案12】:

这是一种平滑的方法,可以处理具有很大深度的关联数组,并且不会覆盖不在数组中的对象属性。

    <?php

    function setPropsViaArray( $a, $o )
    
        foreach ( $a as $k => $v )
        
            if ( is_array( $v ) )
            
                $o->$k = setPropsViaArray( $v, ! empty ( $o->$k ) ? $o->$k : new stdClass() );
            
            else
            
                $o->$k = $v;
            
        
        return $o;
    ;

    setPropsViaArray( $newArrayData, $existingObject );

【讨论】:

【参考方案13】:

晚了,但只想提一下,您可以使用 JSON 编码/解码来完全从/到数组转换:

//convert object $object into array
$array = json_decode(json_encode($object), true);
//convert array $array into object
$object = json_decode(json_encode($array));

json_encode 和 json_decode 函数从 php 5.2 开始可用

【讨论】:

问题本身已经提到了这一点,甚至在这个问题上还有another answer from 2012。【参考方案14】:

编辑:这个函数是从对象到数组的转换。

来自https://forrst.com/posts/PHP_Recursive_Object_to_Array_good_for_handling-0ka

protected function object_to_array($obj)

    $arrObj = is_object($obj) ? get_object_vars($obj) : $obj;
    foreach ($arrObj as $key => $val) 
            $val = (is_array($val) || is_object($val)) ? $this->object_to_array($val) : $val;
            $arr[$key] = $val;
    
    return $arr;

【讨论】:

因为我误解了问题,这个fn是从对象到数组的转换。我会更新我的答案。【参考方案15】:

我正在寻找一种类似于json_decode(json_encode($array))的方式

这里大多数其他递归函数的问题是它们还将顺序数组转换为对象。但是,默认情况下 JSON 变体不会执行此操作。它只将关联数组转换为对象。

以下实现对我来说就像 JSON 变体一样:

function is_array_assoc ($arr) 
    if (!is_array($arr)) return false;
    foreach (array_keys($arr) as $k => $v) if ($k !== $v) return true;
    return false;


// json_decode(json_encode($array))
function array_to_object ($arr) 
    if (!is_array($arr) && !is_object($arr)) return $arr;
    $arr = array_map(__FUNCTION__, (array)$arr);
    return is_array_assoc($arr) ? (object)$arr : $arr;


// json_decode(json_encode($array, true))
// json_decode(json_encode($array, JSON_OBJECT_AS_ARRAY))
function object_to_array ($obj) 
    if (!is_object($obj) && !is_array($obj)) return $obj;
    return array_map(__FUNCTION__, (array)$obj);

如果你想将函数作为一个类:

class ArrayUtils 
    public static function isArrAssoc ($arr) 
        if (!is_array($arr)) return false;
        foreach (array_keys($arr) as $k => $v) if ($k !== $v) return true;
        return false;
    

    // json_decode(json_encode($array))
    public static function arrToObj ($arr) 
        if (!is_array($arr) && !is_object($arr)) return $arr;
        $arr = array_map([__CLASS__, __METHOD__], (array)$arr);
        return self::isArrAssoc($arr) ? (object)$arr : $arr;
    

    // json_decode(json_encode($array, true))
    // json_decode(json_encode($array, JSON_OBJECT_AS_ARRAY))
    public static function objToArr ($obj) 
        if (!is_object($obj) && !is_array($obj)) return $obj;
        return array_map([__CLASS__, __METHOD__], (array)$obj);
    

如果有人发现任何错误,请告诉我。

【讨论】:

【参考方案16】:

我能想到的最短的:

array_walk_recursive($obj, function (&$val)  if (is_object($val)) $val = get_object_vars($val); );

【讨论】:

这是为了将对象转换为数组,而不是相反。

以上是关于PHP - 递归数组到对象?的主要内容,如果未能解决你的问题,请参考以下文章

js递归(二)——合并多维数组

从 SimpleXML 对象到数组的递归转换

php实现快速排序

PHP递归函数将多维数组转换为xml

php实现快速排序

php如何判断数组是一维还是多维