문제

이상한 문제가 발생했으며 어떻게 고치는 지 잘 모르겠습니다. 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() );
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top