具有魔法属性的类上的json_encode

时间:2010-02-13 15:52:23

标签: php json

我正在使用json_encode__get尝试__set一组具有魔法属性的对象。 json_encode完全忽略这些,导致一个空对象数组(所有常规属性都是privateprotected)。

所以,想象一下这个课程:

class Foo
{
    public function __get($sProperty)
    {
        if ($sProperty == 'foo')
        {
            return 'bar!';
        }
        return null;
    }
}

$object = new Foo();
echo $object->foo; // echoes "foo"
echo $object->bar; // warning
echo json_encode($object); // "{}"

我已尝试为该课程实施IteratorAggregateSerializable,但json_encode仍未看到我的魔法属性。由于我正在尝试对这些对象的数组进行编码,因此该类上的AsJSON() - 方法也不起作用。

更新!看来问题很容易被误解。 如何判断json_encode存在哪些“魔法属性”? IteratorAggregate无效。

BTW:term from the PHP documentation是“动态实体”。魔术属性是否真正存在是在争论语义。

8 个答案:

答案 0 :(得分:23)

PHP 5.4有一个名为JsonSerializable的接口。它有一个方法jsonSerialize,它将返回要编码的数据。通过实施它可以很容易地解决这个问题。

答案 1 :(得分:5)

来自你的评论:

  

我在问魔法属性,而不是方法。 - Vegard Larsen 1分钟前

没有神奇的属性

您现在所拥有的只是一种神奇的方法,当您尝试访问不可见的属性时会调用它。

让我们再说一遍

$ obj-> foo不存在

魔术方法的实现方式并不是PHP的关注点,它无法神奇地知道你正在使用魔法来“神奇地”使它看起来像$obj->foo

如果某个属性不存在,则json_encode时它不会被放入该对象。

此外,即使json_encode知道__get处于活动状态,也不知道使用什么值来调用它。

答案 2 :(得分:3)

json_encode()不会“询问”任何接口的对象。它通过调用obj-> get_properties()直接获取表示对象属性的HashTable指针。然后,它在此HashTable上迭代(再次直接,不使用Traversable,Iterator等接口),并处理标记为public的元素。请参阅ext / json / json.c中的static void json_encode_array() 这使得无法在json_encode()的结果中显示属性,但不能以$ obj-> propname的形式访问。

编辑:我没有对它进行过多次测试而忘记了“高性能”,但您可能希望从

开始
interface EncoderData {
  public function getData();
}

function json_encode_ex_as_array(array $v) {
  for($i=0; $i<count($v); $i++) {
    if ( !isset($v[$i]) ) {
      return false;
    }
  }
  return true;
}

define('JSON_ENCODE_EX_SCALAR', 0);
define('JSON_ENCODE_EX_ARRAY', 1);
define('JSON_ENCODE_EX_OBJECT', 2);
define('JSON_ENCODE_EX_EncoderDataObject', 3);

function json_encode_ex($v) {
  if ( is_object($v) ) {
    $type = is_a($v, 'EncoderData') ? JSON_ENCODE_EX_EncoderDataObject : JSON_ENCODE_EX_OBJECT;
  }
  else if ( is_array($v) ) {
    $type = json_encode_ex_as_array($v) ? JSON_ENCODE_EX_ARRAY : JSON_ENCODE_EX_OBJECT;
  }
  else {
    $type = JSON_ENCODE_EX_SCALAR;
  }

  switch($type) {
    case JSON_ENCODE_EX_ARRAY: // array [...]
      foreach($v as $value) {
        $rv[] = json_encode_ex($value);
      }
      $rv = '[' . join(',', $rv) . ']';
      break;
    case JSON_ENCODE_EX_OBJECT: // object { .... }
      $rv = array();
      foreach($v as $key=>$value) {
        $rv[] = json_encode((string)$key) . ':' . json_encode_ex($value);
      }
      $rv = '{' . join(',', $rv) .'}';
      break;
    case JSON_ENCODE_EX_EncoderDataObject:
      $rv = json_encode_ex($v->getData());
      break;
    default:
      $rv = json_encode($v);
  }
  return $rv;
}

class Foo implements EncoderData {
  protected $name;
  protected $child;

  public function __construct($name, $child) {
    $this->name = $name;
    $this->child = $child;

  }
  public function getData() {
    return array('foo'=>'bar!', 'name'=>$this->name, 'child'=>$this->child);
  }
}


$data = array();
for($i=0; $i<10; $i++) {
  $root = null;
  foreach( range('a','d') as $name ) {
    $root = new Foo($name, $root);
  }
  $data[] = 'iteration '.$i;
  $data[] = $root;
  $root = new StdClass;
  $root->i = $i;
  $data[] = $root;
}
$json = json_encode_ex($data);
echo $json, "\n\n\n";
$data = json_decode($json);
var_dump($data);

至少存在一个缺陷:它不处理递归,例如

$obj = new StdClass;
$obj->x = new StdClass;
$obj->x->y = $obj;
echo json_encode($obj); // warning: recursion detected...
echo json_encode_ex($obj); // this one runs until it hits the memory limit

答案 3 :(得分:2)

这是一个令人难以置信的老线程,但是不要混淆其他对魔术方法如何用于获取/设置PHP属性以及它如何影响JSON编码感兴趣的海报,这是我的理解。

在类中包含__get和__set方法会为您提供一个接口来处理给定类的动态命名属性。大多数人定义了一个内部关联数组来执行内务处理。

您的foo / bar示例未以JSON编码显示的原因是您只是创建一个返回值的关联。它不是在对象本身中为该值设置属性。

如果你做了类似的事情:

公共函数集($ name,$ value){      $ this-&gt; data [$ name] = $ value; }

然后如果你打电话:

$ foo = new ObjectClass; $ foo-&gt;设置( '富', '酒吧');

现在该数组中有一个元素$ data ['foo'] ='bar' 如果对对象进行json_encode,那么将表示该关系。

json_encode不编码方法,只编码属性。没有办法绕过它。对于你的代码,你的魔法__get的功能等价物将是在构造函数中包含对set的调用,它“硬连接”名为“foo”的属性的值。

最终,我不知道你正在挣扎的具体细节,因为你没有提供太多的细节。但是,json_encoding对象或数组只会为您提供可用的属性列表。如果你必须通过一个口译功能并以某种方式依赖它,这是一个不同的问题,我很遗憾没有答案。

我希望这有帮助,即使它是降级线程的一部分,具有讽刺意味的是,IMO包含解决方案。

答案 4 :(得分:1)

我在对象上创建一个方法来返回内部数组。

class Foo
{
    private $prop = array();

    public function __get($sProperty)
    {
       return $this->prop[$sProperty];
    }

    public function __set($sProperty, $value)
    {
       $this->prop[$sProperty] = $value;
    }

    public function getJson(){
        return json_encode($this->prop);
    }
}



$f = new Foo();
$f->foo = 'bar';
$json = $f->getJson();

答案 5 :(得分:0)

这是正确的行为
JSON只能包含数据而不是方法 - 它意味着与语言无关,因此编码对象方法没有意义。

答案 6 :(得分:0)

怎么知道你的房产?

$object = new Foo();
echo $object->foo; // how do you know to access foo and not oof?
                   // how do you expect json_encode to know to access foo?

魔术方法是语法糖,并且在你使用它们时大多会反击。这是一个这样的案例。

echo json_encode($object); // "{}"

当然它是空的,$ object没有公共属性,只是一种神奇的方法和随之而来的语法糖(变坏了)

答案 7 :(得分:0)

我发现以下内容对我有用,可以创建具有动态属性的类并使用json_encode输出它们。也许它可以帮助其他人。

http://krisjordan.com/dynamic-properties-in-php-with-stdclass