我正在使用json_encode
和__get
尝试__set
一组具有魔法属性的对象。 json_encode
完全忽略这些,导致一个空对象数组(所有常规属性都是private
或protected
)。
所以,想象一下这个课程:
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); // "{}"
我已尝试为该课程实施IteratorAggregate
和Serializable
,但json_encode
仍未看到我的魔法属性。由于我正在尝试对这些对象的数组进行编码,因此该类上的AsJSON()
- 方法也不起作用。
更新!看来问题很容易被误解。 如何判断json_encode
存在哪些“魔法属性”? IteratorAggregate
无效。
BTW:term from the PHP documentation是“动态实体”。魔术属性是否真正存在是在争论语义。
答案 0 :(得分:23)
PHP 5.4有一个名为JsonSerializable
的接口。它有一个方法jsonSerialize
,它将返回要编码的数据。通过实施它可以很容易地解决这个问题。
答案 1 :(得分:5)
来自你的评论:
我在问魔法属性,而不是方法。 - Vegard Larsen 1分钟前
您现在所拥有的只是一种神奇的方法,当您尝试访问不可见的属性时会调用它。
让我们再说一遍
魔术方法的实现方式并不是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