【问题标题】:Extending json_encode for nested objects为嵌套对象扩展 json_encode
【发布时间】:2012-09-28 22:46:46
【问题描述】:

我正在使用 PHP 5.2.x 并希望仅使用私有成员对我的自定义 PHP 类的对象进行编码。其中一个私有成员是另一个自定义类的对象数组。

我尝试了https://stackoverflow.com/a/7005915/17716 中概述的解决方案,但这显然不能递归地工作。我能看到的唯一解决方案是以某种方式扩展 json_encode 方法,以便调用 json_encode 方法的类版本而不是默认方法。

供参考,代码如下:

Class A {
    private $x;
    private $y;
    private $z;
    private $xy;
    private $yz;
    private $zx;

    public function f1() {
        ...
    }

    public function f2() {
        ...
    }
    .
    .
    .
    public function getJSONEncode() {
         return json_encode(get_object_vars($this));
    }
}

class B {

    private $p; //This finally stores objects of class A
    private $q;
    private $r;

    public function __construct() {
            $this->p = array();
    }

    public function fn1() {
        ...
    }

    public function fn2() {
        ...
    }

    .
    .
    .

    public function getJSONEncode() {
         return json_encode(get_object_vars($this));
    }

}

class C {

    private $arr;

    public function __construct() {
            $this->arr = array();
    }

    public function fillData($data) {
        $obj = new B();
        //create objects of class B and fill in values for all variables
        array_push($this->arr, $obj)
    }

    public function returnData() {
          echo $this->arr[0]->getJSONEncode(); //Edited to add

    }

}

实现这种嵌套 json 编码的最佳方法是什么?

编辑添加:

returnData 方法执行时我得到的输出是:

{"p":[{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}],"q":"Lorem Ipsum","r":"Dolor Sit Amet"}

【问题讨论】:

  • 我在这里没有看到任何嵌套的 json .. Class A { class PostAttributes { 无效.. 我不确定我是否完全得到你想要的
  • 抱歉,感谢您接听。 “Class PostAttributes {”是复制粘贴错误。还添加了数组 P 包含 A 类对象的注释
  • 你到底在执行什么......它不在上面的代码中
  • 您的代码格式有点偏离。
  • @Baba:不确定我是否理解您的问题...您能详细说明一下吗?

标签: php object json


【解决方案1】:

虽然我相信你最好为你的每个类编写一个适当的导出/编码函数(它会从私有和公共值构造一个公共对象,只是为了编码 - 你可以非常聪明并使用php 的 reflection 能力) - 相反 - 您可以使用以下代码:

/// example class B
class TestB {
  private $value = 123;
}

/// example class A
class TestA {
  private $prop;
  public function __construct(){
    $this->prop = new TestB();
  }
}

function json_encode_private($obj){
  /// export the variable to find the privates
  $exp = var_export($obj, true);
  /// get rid of the __set_state that only works 5.1+
  $exp = preg_replace('/[a-z0-9_]+\:\:__set_state\(/i','((object)', $exp);
  /// rebuild the object
  eval('$enc = json_encode('.$exp.');');
  /// return the encoded value
  return $enc;
}

echo json_encode_private(new TestA());

/// {"prop":{"value":123}}

所以上面应该可以工作,但我不建议在 php 中的任何地方使用eval - 只是因为我总是在远处安静地听到警铃:)

更新

刚刚想到什么可以让这更安全一点,而不是使用eval,你可以使用create_function,这会限制它的一些创造能力,或者至少是这些能力的范围......

function json_encode_private($obj){
  $exp = var_export($obj, true);
  $exp = preg_replace('/[a-z0-9_]+\:\:__set_state\(/i','((object)', $exp);
  $enc = create_function('','return json_encode('.$exp.');');
  return $enc();
}

更新 2

有机会尝试另一种将具有私有属性的对象转换为具有公共属性的对象的方法 - 仅使用一个简单的函数(并且没有 eval)。以下内容需要在您使用的任何版本的 PHP 上进行测试,因为它的行为 - 再次 - 可能不可靠....由于奇怪的 \0Class Name\0 前缀在转换后的私有属性中(参见代码中的 cmets)

有关这种奇怪的前缀行为的更多信息:
http://uk3.php.net/language.types.array.php#language.types.array.casting

无论如何,使用测试类:

class RandomClass {
  private $var = 123;
  private $obj;
  public function __construct(){
    $this->obj = (object) null;
    $this->obj->time = time();
  }
}

我们可以使用以下函数将其转换为公共对象:

function private_to_public( $a ){
  /// grab our class, convert our object to array, build our return obj
  $c = get_class( $a ); $b = (array) $a; $d = (object) null;
  /// step each property in the array and move over to the object
  /// usually this would be as simple as casting to an object, however
  /// my version of php (5.3) seems to do strange things to private 
  /// properties when casting to an array... hence the code below:
  foreach( $b as $k => $v ){
    /// for some reason private methods are prefixed with a \0 character
    /// and then the classname, followed by \0 before the actual key value. 
    /// This must be some kind of internal protection causing private  
    /// properties to be ignored. \0 is used by some languges to terminate  
    /// strings (not php though, as far as i'm aware).
    if ( ord($k{0}) === 0 ) {
      /// trim off the prefixed weirdnesss..?!
      $e = substr($k, 1 + strlen($c) + 1);
      /// unset the $k var first because it will remember the \0 even 
      /// if other values are assigned to it later on....?!
      unset($k); $k = $e;
    }
    /// so if we have a key, either public or private - set our value on
    /// the destination object.
    if ( $k !== '' && $k !== NULL && $k !== FALSE )  {
      $d->{$k} = $v;
    }
  }
  return $d;
}

所以如果我们把它们放在一起:

$a = new RandomClass();

echo json_encode( private_to_public( $a ) );

/// {"var":123,"obj":{"time":1349777323}}

再一次,您最好/最可靠的选择是为每个类定制您的转换方法,或者使用Class Reflection 创建某种通用解决方案,但后者比 StackOverflow 答案更复杂,更复杂。 . 至少在我有空闲时间的情况下;)

更多信息

上面的代码在尝试从任何地方访问对象时都会起作用,实现它的原因是因为我的第一次尝试显然是使用以下内容:

echo json_encode( get_object_vars($a) );

/// you will get {} which isn't what you expect

看来,如果你想使用get_object_vars,你必须从可以访问所有属性的上下文中使用它,即从你要公开的类内部:

public function getAllProperties(){
  return get_object_vars( $this );
}

所以,假设我们将上述内容添加到 RandomClass 定义中:

echo json_encode( $a->getAllProperties() );

/// you will get {"var":123,"obj":{"time":1349777323}}

这是可行的,因为类的成员可以访问类的所有公共或私有属性……正如我所说,以这种方式工作要优越得多;优越,但并非总是可能的。

【讨论】:

  • 对不起,我不完全熟悉 PHP 中的 OO 工作,但无论如何我不能扩展 json_encode 方法(或我的类),以便在尝试编码时,它们会自动被叫? - 类似于 JSONSerialize 使之成为可能的东西,但在 PHP 5.4 及更高版本中
  • 不,我不知道,我猜这是创建JSONSerialize 的三个原因。有 __sleep__wakeup 的神奇功能,但它们仅适用于 serializeunserialize。还有神奇的功能__set_state,但它是与var_export一起使用的。 php.net/manual/en/language.oop5.magic.php - 刚刚也发现了另一个同样的问题stackoverflow.com/questions/8778020/… (那里也没有非黑客的答案)
  • 感谢您提供各种解决方法。我还将尝试推动我的托管服务提供商升级他们的 PHP 版本,以便我可以使用更简单的 JSONSerialize 接口。直到他们,你的答案是要走的路:)
  • @AJ。没问题...您的问题迫使我学习有关get_object_vars 的新知识,所以一切都很好!是的,要求他们升级听起来像是一个计划 :) 唯一可能的问题是 php 5.4 于 2012 年 3 月 1 日发布,我所知道的大多数主机只会在发布至少一段时间后才考虑升级年 - 如果不是更多。如果你能得到一个专用服务器,你可以做你喜欢做的事,虽然那会是一个相当昂贵的 json_encode 函数;)
猜你喜欢
  • 1970-01-01
  • 2018-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-02
  • 2020-12-22
相关资源
最近更新 更多