Frage

Ich habe in ein ungeradees Problem laufen und ich bin nicht sicher, wie es zu beheben. Ich habe mehrere Klassen, die alle PHP-Implementierungen von JSON-Objekte sind. Hier eine Darstellung der Ausgabe

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;

Die Ausgabe von dieser ist

[{},{}]

Wenn die gewünschte Ausgang

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

Das Problem ist, dass ich auf dem __toString wurde unter Berufung () hook meine Arbeit für mich zu tun. Aber es kann nicht, weil die serialize die json_encode () verwendet wird __toString nicht () aufrufen. Wenn es ein verschachteltes Objekt trifft serialisiert es einfach öffentliche Eigenschaften nur.

So werden die Frage dann diese: Gibt es eine Möglichkeit ich eine verwaltete Schnittstelle zu JSON-Klassen entwickeln können, die sowohl mich für Eigenschaften verwenden Getter und Setter können, sondern ermöglicht auch mir das JSON Serialisierungsverhalten erhalte ich wünsche

Wenn das nicht klar ist, hier ist ein Beispiel für eine Implementierung, die wird nicht Arbeit, da die __set () Haken nur für die anfängliche Zuweisung genannt wird

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;

Ich glaube, ich auch so etwas tun könnte

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;

Aber dann hätte ich auf der Sorgfalt der anderen Entwickler verlassen, um die Setter zu verwenden. - Ich nicht die Einhaltung programmtically mit dieser Lösung erzwingen

---> EDIT <---

Jetzt mit einem Test von Rob Elsner Antwort

<?php

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

    public function getIterator()
    {
        echo __METHOD__;
    }
}

echo json_encode( new a );

Wenn Sie dies ausführen, können Sie sehen, dass der getIterator () -Methode nicht immer aufgerufen.

War es hilfreich?

Lösung

Eine späten Antworten, aber vielleicht für andere mit dem gleichen Problem nützlich sein.

In PHP <5.4.0 json_encode ruft keine Methode aus dem Objekt. Das gilt für getIterator, __serialize, etc ...

In PHP> V5.4.0 jedoch eine neue Schnittstelle eingeführt wurde, genannt JsonSerializable .

Es steuert im Grunde das Verhalten des Objekts, wenn json_encode für das Objekt aufgerufen wird.


Fiddle (Na ja, eigentlich ist PHP CodePad, aber gleiche ...)

Beispiel: :

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);

Ausgänge:

  

string (29) "[{" foo ":" bar "}, {" foo ":" bar "}]"

Andere Tipps

Ist das nicht Ihre Antwort in der PHP-Dokumentation für json_encode ?

  

Für alle, die sich mit dem Problem der privaten Eigenschaften laufen hat nicht hinzugefügt werden, können Sie einfach die IteratorAggregate Schnittstelle mit dem getIterator () -Methode implementieren. Fügen Sie die gewünschten Eigenschaften in der Ausgabe in ein Array aufgenommen werden in dem getIterator () -Methode und gibt es zurück.

In PHP> V5.4.0 können Sie die Schnittstelle implementieren genannt JsonSerializable wie in die Antwort von Tivie .

Für die von uns mit PHP <5.4.0 können Sie eine Lösung verwendet werden, die verwendet get_object_vars() aus dem Objekt selbst und speist dann jene auf json_encode() . Das ist, was ich in dem folgenden Beispiel geschehen, die __toString() Methode verwendet, so dass, wenn ich warf das Objekt als String, habe ich eine JSON codierte Darstellung zu bekommen.

Ebenfalls enthalten ist eine Implementierung des IteratorAggregate Schnittstelle mit seinen < a href = „http://php.net/manual/en/iteratoraggregate.getiterator.php“ rel = „nofollow noreferrer“> getIterator() Methode, so dass wir über die Objekteigenschaften laufen, als ob sie eine waren Array.

<?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;

?>

Dieser Code erzeugt die folgende Ausgabe:

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

Auch wenn die geschützte Variable öffentlich statt geschützt war, werden Sie die gewünschte Eingabe nicht haben das gesamte Objekt wie diese, da dies Ausgabe:

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

Statt:

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

Es wird höchstwahrscheinlich Niederlage Ihres Zweck, aber ich bin eher geneigt, json in der ursprünglichen Klasse mit einem Standard-Getter und direkt für die Werte Aufruf zu konvertieren

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

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

Dann könnte man mit ihnen tun, was Sie wünschen, auch die Werte in einem zusätzlichen Array wie Ihre Klasse A Verschachtelung ist, aber unter Verwendung von json_decode .. es noch etwas schmutzig fühlt, aber funktioniert.

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 );
    }
}

In der Dokumentation rel="nofollow gibt es einige Antworten auf diese Problem (auch wenn ich von ihnen nicht wie die meisten, die Serialisierung + die Eigenschaften Strippen macht mich schmutzig fühlen).

Sie haben Recht, die __toString () der Klasse B wird nicht aufgerufen werden, weil es keinen Grund zu ist. So ist es zu nennen, können Sie eine Besetzung verwenden

class A
{
    protected $a;

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

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

Hinweis: die (string) gegossen, bevor die neuen B ... dies wird die _toString () -Methode der B-Klasse aufrufen, aber es wird Sie nicht bekommen, was Sie wollen, weil Sie in der klassischen „Doppel laufen encoding“Probleme, weil die Anordnung in der B-Klasse _toString codiert ist () Methode, und es wird kodiert erneut in der A Klasse _toString () Methode.

So gibt es eine Auswahl der Decodierung des Ergebnisses nach der Besetzung, das heißt:

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

oder Sie gehen zu müssen, um das Array zu erhalten, indem eine toArray () -Methode in der B-Klasse erstellen, die die gerade Array zurückgibt. Welche wird einige Codes auf die Zeile oben hinzufügen, weil Sie nicht direkt einen PHP-Konstruktor verwenden können (Sie einen neuen B nicht tun können () -> toArray ();) Also man so etwas haben könnte:

$b1 = new B;
$b2 = new B;
$this->a = array( $b1->toArray(), $b2->toArray() );
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top