php : __toString () 및 json_encode ()가 잘 재생되지 않음
-
03-07-2019 - |
문제
이상한 문제가 발생했으며 어떻게 고치는 지 잘 모르겠습니다. 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 클래스에 대한 관리 된 인터페이스를 개발할 수있는 방법이 있습니까?
그것이 명확하지 않다면, 다음은 구현의 예입니다. 습관 __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;
그러나 나는 다른 개발자들이 세터를 사용하기 위해 다른 개발자들의 근면에 의존해야 할 것입니다.이 솔루션으로 프로그램을 고착시킬 수는 없습니다.
---> 편집 <---
이제 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 <5.4.0 json_encode
객체에서 어떤 메소드도 호출하지 않습니다. 그것은 getiterator, __serialize 등에 유효합니다 ...
그러나 PHP> v5.4.0에서는 새로운 인터페이스가 소개되었습니다. jsonserializable.
기본적으로 객체의 동작을 제어합니다 json_encode
그 대상에 호출됩니다.
깡깡이 (글쎄, 실제로 PHP 코드 패드이지만 같은 것 ...)
예시:
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);
출력 :
문자열 (29) [{"foo ":"bar "}, {"foo ":"bar "}] "
다른 팁
당신의 대답이 아닙니다 JSON_ENCODE 용 PHP 문서?
추가되지 않은 개인 속성 문제가 발생한 사람이라면 GetIterator () 메소드와 함께 IeratorAggregate 인터페이스를 구현할 수 있습니다. 출력에 포함하려는 속성을 getiterator () 메소드의 배열에 추가하고 반환하십시오.
PHP> v5.4.0에서는 다음 인터페이스를 구현할 수 있습니다. JsonSerializable
설명대로 대답 ~에 의해 티비.
PHP <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 () 메소드를 호출하지만 클래식 "이중 인코딩"문제가 발생하기 때문에 원하는 것을 얻지 못할 것입니다. , 배열은 b class _toString () 메소드로 인코딩되므로 인코딩됩니다. 다시 A 클래스 _toString () 메소드에서.
따라서 캐스트 후 결과를 디코딩 할 수 있습니다.
$this->a = array( json_decode((string)new B), json_decode((string)new B) );
또는 직선 배열을 반환하는 B 클래스에서 ToArray () 메소드를 작성하여 배열을 가져와야합니다. PHP 생성자를 직접 사용할 수 없기 때문에 위의 줄에 일부 코드가 추가됩니다 (새 B ()-> ToArray ();)를 수행 할 수 없으므로 다음과 같은 것을 가질 수 있습니다.
$b1 = new B;
$b2 = new B;
$this->a = array( $b1->toArray(), $b2->toArray() );