我遇到了一个奇怪的问题,我不知道如何解决它。我有几个类都是JSON对象的PHP实现。这是'问题的说明

class A
{
    protected $a;

    public function __construct()
    {
        $this->a = array( new B, new B );
    }

    public function __toString()
    {
        return json_encode( $this->a );
    }
}

class B
{
    protected $b = array( 'foo' => 'bar' );

    public function __toString()
    {
        return json_encode( $this->b );
    }
}

$a = new A();

echo $a;

此输出是

[{},{}]

所需的输出

[{"foo":"bar"},{"foo":"bar"}]

问题是我依靠__toString()钩子来为我做我的工作。但它不能,因为json_encode()使用的序列化不会调用__toString()。遇到嵌套对象时,它只是序列化公共属性。

那么,问题就变成了这样:有没有办法可以开发一个JSON类的托管接口,它既可以让我使用setter和getter来获取属性,也可以让我获得我想要的JSON序列化行为?

如果不清楚,这里是一个不会工作的实现示例,因为__set()挂钩只被调用初始赋值

class a
{
    public function __set( $prop, $value )
    {
        echo __METHOD__, PHP_EOL;
        $this->$prop = $value;
    }

    public function __toString()
    {
        return json_encode( $this );
    }
}

$a = new a;
$a->foo = 'bar';
$a->foo = 'baz';

echo $a;

我想我也可以做这样的事情

class a
{
    public $foo;

    public function setFoo( $value )
    {
        $this->foo = $value;
    }

    public function __toString()
    {
        return json_encode( $this );
    }
}

$a = new a;
$a->setFoo( 'bar' );

echo $a;

但后来我不得不依赖其他开发人员的勤奋来使用这些解决方案 - 我不能强制使用这个解决方案加入程序。

<!>

<强> --- GT;编辑<!> lt; ---

现在测试一下Rob Elsner的回应

<?php

class a implements IteratorAggregate 
{
    public $foo = 'bar';
    protected $bar = 'baz';

    public function getIterator()
    {
        echo __METHOD__;
    }
}

echo json_encode( new a );

执行此操作时,您可以看到不会调用getIterator()方法。

有帮助吗?

解决方案

答案较晚,但对于遇到同样问题的其他人可能会有用。

PHP中<!> lt; 5.4.0 json_encode 不会从对象调用任何方法。这对getIterator,__ serialize等有效......

在PHP中<!> gt;然而,v5.4.0引入了一个名为 JsonSerializable 的新界面。

当在该对象上调用 <=> 时,它基本上控制了对象的行为。


小提琴 (嗯,实际上是PHP CodePad,但同样的事情......)

示例

class A implements JsonSerializable
{
    protected $a = array();

    public function __construct()
    {
        $this->a = array( new B, new B );
    }

    public function jsonSerialize()
    {
        return $this->a;
    }
}

class B implements JsonSerializable
{
    protected $b = array( 'foo' => 'bar' );

    public function jsonSerialize()
    {
        return $this->b;
    }
}


$foo = new A();

$json = json_encode($foo);

var_dump($json);

<强>输出:

  

string(29)<!> quot; [{<!> quot; foo <!> quot;:<!> quot; bar <!> quot;},{<!> quot; foo <!> quot ;:<!>; <!>; <!> QUOT酒吧QUOT}] QUOT;

其他提示

json_encode的 PHP文档中的答案不是的?

  

对于遇到未添加私有属性问题的任何人,您只需使用getIterator()方法实现IteratorAggregate接口即可。将要包含在输出中的属性添加到getIterator()方法的数组中并返回它。

在PHP中<!> gt; v5.4.0你可以实现名为 JsonSerializable 的界面,如< Tivie 答案 >

对于我们这些使用PHP <!> lt; 5.4.0您可以使用 get_object_vars() 从对象本身内部,然后将其提供给 json_encode() 。这就是我在下面的例子中使用__toString()方法所做的,所以当我将对象转换为字符串时,我得到一个JSON编码表示。

还包括 IteratorAggregate 界面的实现,它的 getIterator() 方法,以便我们可以迭代对象属性如果他们是阵列。

<?php
class TestObject implements IteratorAggregate {

  public $public = "foo";
  protected $protected = "bar";
  private $private = 1;
  private $privateList = array("foo", "bar", "baz" => TRUE);

  /**
   * Retrieve the object as a JSON serialized string
   *
   * @return string
   */
  public function __toString() {
    $properties = $this->getAllProperties();

    $json = json_encode(
      $properties,
      JSON_FORCE_OBJECT | JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT
    );

    return $json;
  }

  /**
   * Retrieve an external iterator
   *
   * @link http://php.net/manual/en/iteratoraggregate.getiterator.php
   * @return \Traversable
   *  An instance of an object implementing \Traversable
   */
  public function getIterator() {
    $properties = $this->getAllProperties();
    $iterator = new \ArrayIterator($properties);

    return $iterator;
  }

  /**
   * Get all the properties of the object
   *
   * @return array
   */
  private function getAllProperties() {
    $all_properties = get_object_vars($this);

    $properties = array();
    while (list ($full_name, $value) = each($all_properties)) {
      $full_name_components = explode("\0", $full_name);
      $property_name = array_pop($full_name_components);
      if ($property_name && isset($value)) $properties[$property_name] = $value;
    }

    return $properties;
  }

}

$o = new TestObject();

print "JSON STRING". PHP_EOL;
print "------" . PHP_EOL;
print strval($o) . PHP_EOL;
print PHP_EOL;

print "ITERATE PROPERTIES" . PHP_EOL;
print "-------" . PHP_EOL;
foreach ($o as $key => $val) print "$key -> $val" . PHP_EOL;
print PHP_EOL;

?>

此代码生成以下输出:

JSON STRING
------
{"public":"foo","protected":"bar","private":1,"privateList":{"0":"foo","1":"bar","baz":true}}

ITERATE PROPERTIES
-------
public -> foo
protected -> bar
private -> 1
privateList -> Array

即使您的受保护变量是公共的而不是受保护的,您也没有所需的输入,因为这将输出整个对象,如下所示:

[{"b":{"foo":"bar"}},{"b":{"foo":"bar"}}]

而不是:

[{"foo":"bar"},{"foo":"bar"}]

它很可能会破坏你的目的,但我更倾向于使用默认的getter转换为原始类中的json并直接调用值

class B
{
    protected $b = array( 'foo' => 'bar' );

    public function __get($name)
    {
        return json_encode( $this->$name );
    }
}

然后你可以随心所欲地使用它们,甚至将值嵌套在类A的附加数组中,但是使用json_decode ..它仍然感觉有些脏,但是有效。

class A
{
    protected $a;

    public function __construct()
    {
        $b1 = new B;
        $b2 = new B;
        $this->a = array( json_decode($b1->b), json_decode($b2->b) );
    }

    public function __toString()
    {
        return json_encode( $this->a );
    }
}

文档中,有一些回复问题(即使我不喜欢大多数问题,序列化+剥离属性会让我觉得很脏)。

你是对的,没有调用类B的__toString(),因为没有理由。所以要调用它,你可以使用强制转换

class A
{
    protected $a;

    public function __construct()
    {
        $this->a = array( (string)new B, (string)new B );
    }

    public function __toString()
    {
        return json_encode( $this->a );
    }
}

注意:在新B之前的(字符串)强制转换...这将调用B类的_toString()方法,但它不会得到你想要的东西,因为你将遇到经典的<! > quot; double encoding <!> quot;问题,因为数组是在B类_toString()方法中编码的,它将在A类_toString()方法中再次编码

因此可以选择在演员表后解码结果,即:

 $this->a = array( json_decode((string)new B), json_decode((string)new B) );

或者你需要通过在B类中创建一个返回直数组的toArray()方法来获取数组。这将在上面的行中添加一些代码,因为你不能直接使用PHP构造函数(你不能做一个新的B() - <!> gt; toArray();)所以你可以有类似的东西:

$b1 = new B;
$b2 = new B;
$this->a = array( $b1->toArray(), $b2->toArray() );
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top