سؤال

لقد واجهت مشكلة غريبة و انا غير متأكد من كيفية إصلاحه.لدي العديد من فئات كل PHP تطبيقات كائنات JSON.هنا مثال على المشكلة

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().عندما واجه متداخلة كائن ببساطة يسلسل الممتلكات العامة فقط.

إذن السؤال ثم تصبح هذه:هل هناك طريقة يمكنني وضع تمكنت واجهة سلمان الطبقات أن كلا يتيح لي استخدام واضعي و حاصل على خصائص ، ولكن أيضا يسمح لي للحصول على سلمان التسلسل سلوك أنا الرغبة ؟

إذا هذا ليس واضحا ، وهنا مثال على تنفيذ ذلك لن العمل منذ __مجموعة() هوك فقط دعا الأولية المهمة

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;

ولكن بعد ذلك سوف تضطر إلى الاعتماد على الاجتهاد من مطورين آخرين إلى استخدام واضعي - لا أستطيع أن أجبر على الانضمام programmtically مع هذا الحل.

---> تحرير <---

والآن مع اختبار روب إيلسنر رد

<?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, __تسلسل ، الخ...

في PHP > v5.4.0 ومع ذلك واجهة جديدة استحدثت يسمى JsonSerializable.

فإنه يتحكم في الأساس سلوك الكائن عند json_encode ويطلق على هذا الكائن.


كمان (في الواقع هو 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);

النواتج:

سلسلة(29) "[{"فو":"شريط"},{"فو":"شريط"}]"

نصائح أخرى

ليس الجواب في PHP مستندات json_encode?

لأي شخص الذين قد واجهت مشكلة الممتلكات الخاصة لا يتم إضافتها ، يمكنك ببساطة تنفذ في بلدان واجهة مع getIterator الأسلوب ().إضافة خصائص تريد إدراجها في الإخراج في صفيف في getIterator() طريقة وإعادته.

في PHP > v5.4.0 يمكنك تنفيذ واجهة تسمى JsonSerializable كما هو موضح في الجواب قبل Tivie.

بالنسبة لنا باستخدام PHP < 5.4.0 يمكنك استخدام الحل الذي توظف get_object_vars() من داخل الكائن نفسه ثم يغذي تلك json_encode().وهذا هو ما قمت به في المثال التالي باستخدام __toString() طريقة بحيث عندما يلقي الكائن كسلسلة ، أحصل على سلمان ترميز التمثيل.

وشملت أيضا هو تنفيذ 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"}]

فإنه من المرجح أن هزيمة الغرض الخاص بك, ولكن أنا أكثر ميلا إلى تحويل إلى json في الطبقة الأصلية مع الافتراضي جالبة يدعو إلى القيم مباشرة

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

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

ثم هل يمكن أن تفعل معها ما تريد ، حتى تداخل القيم في مجموعة إضافية مثل صفك لا ، ولكن باستخدام 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 );
    }
}

في الوثائق هناك بعض الردود على هذه المشكلة (حتى إذا كنت لا ترغب في معظمها ، التسلسلية + تجريد خصائص يجعلني أشعر القذرة).

كنت على حق __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 );
    }
}

ملاحظة:في (سلسلة) قبل أن يلقي الجديد ب ...هذا استدعاء _toString (طريقة) من الفئة B, لكن لن تحصل على ما تريد لأنك سوف تعمل في الكلاسيكية "مزدوجة ترميز" مشاكل بسبب مجموعة المشفرة في الفئة B _toString (طريقة) ، وسوف يتم ترميز مرة أخرى في فئة _toString الأسلوب ().

حتى لا يكون هناك خيار فك النتيجة بعد المدلى بها ، أي:

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

أو أنك سوف تحتاج إلى الحصول على مجموعة ، من خلال خلق toArray (طريقة) في الفئة B أن ترجع مباشرة مجموعة.والتي سوف تضيف بعض التعليمات البرمجية إلى خط أعلاه لأنه لا يمكنك استخدام PHP منشئ مباشرة (لا يمكنك جديد ب()->toArray () ؛ ) لذلك يمكن أن يكون شيئا مثل:

$b1 = new B;
$b2 = new B;
$this->a = array( $b1->toArray(), $b2->toArray() );
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top