PHP nesnesini JSON'a seri hale getirme


101

Ben dolaşıp edildi Yani php.net yeni tökezledi, PHP JSON itiraz seri hakkında bilgi almak için JsonSerializable Arabirimi . Gerçi sadece PHP> = 5.4 ve bir 5.3.x ortamında çalışıyorum.

Bu tür bir işlevsellik PHP <5.4'e nasıl ulaşılır ?

Henüz JSON ile pek çalışmadım, ancak bir uygulamada bir API katmanını desteklemeye çalışıyorum ve veri nesnesini ( aksi takdirde görünüme gönderilecek olan ) JSON'a dökmek mükemmel olurdu.

Nesneyi doğrudan serileştirmeye çalışırsam, boş bir JSON dizesi döndürür; çünkü json_encode()nesneyle ne yapacağımı bilmediğimi varsayıyorum . Ben yinelemeli bir diziye nesneyi azaltmak ve sonra kodlamak mı o ?


Misal

$data = new Mf_Data();
$data->foo->bar['hello'] = 'world';

echo json_encode($data) boş bir nesne üretir:

{}

var_dump($data) ancak beklendiği gibi çalışır:

object(Mf_Data)#1 (5) {
  ["_values":"Mf_Data":private]=>
  array(0) {
  }
  ["_children":"Mf_Data":private]=>
  array(1) {
    [0]=>
    array(1) {
      ["foo"]=>
      object(Mf_Data)#2 (5) {
        ["_values":"Mf_Data":private]=>
        array(0) {
        }
        ["_children":"Mf_Data":private]=>
        array(1) {
          [0]=>
          array(1) {
            ["bar"]=>
            object(Mf_Data)#3 (5) {
              ["_values":"Mf_Data":private]=>
              array(1) {
                [0]=>
                array(1) {
                  ["hello"]=>
                  string(5) "world"
                }
              }
              ["_children":"Mf_Data":private]=>
              array(0) {
              }
              ["_parent":"Mf_Data":private]=>
              *RECURSION*
              ["_key":"Mf_Data":private]=>
              string(3) "bar"
              ["_index":"Mf_Data":private]=>
              int(0)
            }
          }
        }
        ["_parent":"Mf_Data":private]=>
        *RECURSION*
        ["_key":"Mf_Data":private]=>
        string(3) "foo"
        ["_index":"Mf_Data":private]=>
        int(0)
      }
    }
  }
  ["_parent":"Mf_Data":private]=>
  NULL
  ["_key":"Mf_Data":private]=>
  NULL
  ["_index":"Mf_Data":private]=>
  int(0)
}

Ek

1)

Yani bu sınıf toArray()için tasarladığım işlev Mf_Data:

public function toArray()
{
    $array = (array) $this;
    array_walk_recursive($array, function (&$property) {
        if ($property instanceof Mf_Data) {
            $property = $property->toArray();
        }
    });
    return $array;
}

Bununla birlikte, Mf_Datanesnelerin üst ( içeren ) nesnelerine de bir başvuruları olduğundan , bu özyinelemeyle başarısız olur. _parentReferansı kaldırdığımda bir cazibe gibi çalışıyor .

2)

Sadece takip etmek için, birlikte gittiğim karmaşık bir ağaç düğümü nesnesini dönüştürmek için son işlev şuydu:

// class name - Mf_Data
// exlcuded properties - $_parent, $_index
public function toArray()
{
    $array = get_object_vars($this);
    unset($array['_parent'], $array['_index']);
    array_walk_recursive($array, function (&$property) {
        if (is_object($property) && method_exists($property, 'toArray')) {
            $property = $property->toArray();
        }
    });
    return $array;
}

3)

Biraz daha temiz bir uygulama ile tekrar takip ediyorum. Bir instanceofdenetim için arabirim kullanmak, daha temiz görünmektedir method_exists()( ancak method_exists()çapraz kesim kalıtım / uygulama ).

Kullanımı unset()da biraz dağınık görünüyordu ve görünüşe göre mantığın başka bir yönteme dönüştürülmesi gerekiyor. Ancak, bu uygulama yapar (mülkiyet dizisi kopyalamak nedeniylearray_diff_key bir şey düşünmeye yüzden).

interface ToMapInterface
{

    function toMap();

    function getToMapProperties();

}

class Node implements ToMapInterface
{

    private $index;
    private $parent;
    private $values = array();

    public function toMap()
    {
        $array = $this->getToMapProperties();
        array_walk_recursive($array, function (&$value) {
            if ($value instanceof ToMapInterface) {
                $value = $value->toMap();
            }
        });
        return $array;
    }

    public function getToMapProperties()
    {
        return array_diff_key(get_object_vars($this), array_flip(array(
            'index', 'parent'
        )));
    }

}

4
+1 Güzel soru, bu özelliği henüz bilmiyordum.
44'te takeshin

@takeshin - Yeop, belge sayfasındaki düzenleme tarihi 4 gün önce. Bunu gördüğüme sevindim!
Dan Lugg

2
Buna bakan diğerlerine referans için json_encode nesneleri gayet iyi işleyebilir. Ancak, yalnızca o nesnenin genel üyelerini kodlar. Dolayısıyla, korumalı veya özel sınıf değişkenleriniz varsa, o zaman yayınlanan yöntemlerden birine veya JsonSerializable'a ihtiyacınız vardır.
Matthew Herbst

@MatthewHerbst Kesinlikle. Eski soru artık eski ve <5.4 artık gerçekten bir seçenek değil (veya en azından olmamalı) KesinlikleJsonSerializable
Dan Lugg

Yanıtlar:


45

düzenleme : şu anda 2016-09-24 ve PHP 5.4, 2012-03-01'de yayınlandı ve destek sona erdi . Yine de, bu cevap olumlu oylar alıyor gibi görünüyor. Hala PHP <5.4 kullanıyorsanız, bir güvenlik riski oluşturuyorsunuz ve projenizi bozuyorsunuz . <5.4'te kalmak için ikna edici bir nedeniniz yoksa veya zaten sürüm> = 5.4'ü kullanıyorsanız, bu yanıtı kullanmayın ve sadece PHP> = 5.4 (veya biliyorsunuz, yeni bir tane) kullanın ve JsonSerializable arayüzünü uygulayın


Özel / korumalı olanlar yerine görünür parametrelere sahip getJsonData();bir dizi, stdClassnesne veya başka bir nesneyi döndürecek, örneğin adlandırılmış bir işlev tanımlarsınız ve bir json_encode($data->getJsonData());. Özünde, 5.4'ten itibaren işlevi uygulayın, ancak elle çağırın.

Bunun gibi bir şey get_object_vars(), sınıfın içinden çağrıldığı gibi çalışır ve özel / korumalı değişkenlere erişim sağlar:

function getJsonData(){
    $var = get_object_vars($this);
    foreach ($var as &$value) {
        if (is_object($value) && method_exists($value,'getJsonData')) {
            $value = $value->getJsonData();
        }
    }
    return $var;
}

2
Teşekkürler @Wrikken - Bir nesneyi, içerdiği nesneleri ( görünürlüğü veya türü ne olursa olsun tüm üyeler ) ilişkilendirilebilir bir diziye indirgemek veya onu yazmak için herhangi bir kısayol var mı stdClass? Ben yönünde düşünüyorum Yansıma , ama değilse, ben sadece yinelemeli bunu gerçekleştirmek için bir şeyler anlamaya edeceğiz.
Dan Lugg

Düşünmek uzun bir yol olurdu. Eğer olduğundan içeride sizin sınıf getJsonData()fonksiyonu, sadece diyebiliriz get_object_vars()fazla nesneler için arayan o sonucu üzerinden ve döngü.
Wrikken

Neredeyse hallettim; şimdi sorun özyinelemedir. Her nesnenin bir _parentözelliği vardır, böylece ağacın köke geçilebilir. Bir güncelleme için düzenlememe bakın; belki de bu konu şimdi benim orijinalimden soyutlandığı için başka bir soru sormalıyım.
Dan Lugg

Yürüyüşten unset($array['_parent']);önce basit bir hile yapmalı.
Wrikken

Harika, teşekkürler @Wrikken - $parentKullanıcı verileri olarak bir bağlam nesnesi geçirerek karmaşık eşitlik testlerini denemeye başladım array_walk_recursive(). Basit güzeldir! Ayrıca, $array["\0class\0property"]döküm kullanıyordum çünkü boş bayt kirliliği yüzünden. Sanırım geçiş yapacağım get_object_vars().
Dan Lugg

91

En basit durumlarda tip ipucu çalışmalıdır:

$json = json_encode( (array)$object );

7
Bu, ad alanları ve otomatik yükleyici ile çalışıyorsanız, uzun soluklu / çirkin özellik adları verir.
BetaRide

bu en iyi çözüm, kesin ve öz!
Sujal Mandal

4
daha temiz özellik adları almanın bir yolu var mı?
Christoffer

5
prop adlarının başına neden \ u0000 * \ u0000 ekliyor?
Elia Weiss

1
Özel mülklerde işe yaramaz. Hepiniz en.wikipedia.org/wiki/Open/closed_principle hakkında bilgi edinmelisiniz .
Fabian Picone

19

json_encode()yalnızca genel üye değişkenlerini kodlayacaktır. Bu nedenle, kendi başınıza yapmanız gerektiğinde özel olanı eklemek istiyorsanız (diğerlerinin önerdiği gibi)


9

Aşağıdaki kod, işi yansımayı kullanarak yapıyor. Serileştirmek istediğiniz özellikler için alıcılarınız olduğunu varsayar

    <?php

    /**
     * Serialize a simple PHP object into json
     * Should be used for POPO that has getter methods for the relevant properties to serialize
     * A property can be simple or by itself another POPO object
     *
     * Class CleanJsonSerializer
     */
    class CleanJsonSerializer {

    /**
     * Local cache of a property getters per class - optimize reflection code if the same object appears several times
     * @var array
     */
    private $classPropertyGetters = array();

    /**
     * @param mixed $object
     * @return string|false
     */
    public function serialize($object)
    {
        return json_encode($this->serializeInternal($object));
    }

    /**
     * @param $object
     * @return array
     */
    private function serializeInternal($object)
    {
        if (is_array($object)) {
            $result = $this->serializeArray($object);
        } elseif (is_object($object)) {
            $result = $this->serializeObject($object);
        } else {
            $result = $object;
        }
        return $result;
    }

    /**
     * @param $object
     * @return \ReflectionClass
     */
    private function getClassPropertyGetters($object)
    {
        $className = get_class($object);
        if (!isset($this->classPropertyGetters[$className])) {
            $reflector = new \ReflectionClass($className);
            $properties = $reflector->getProperties();
            $getters = array();
            foreach ($properties as $property)
            {
                $name = $property->getName();
                $getter = "get" . ucfirst($name);
                try {
                    $reflector->getMethod($getter);
                    $getters[$name] = $getter;
                } catch (\Exception $e) {
                    // if no getter for a specific property - ignore it
                }
            }
            $this->classPropertyGetters[$className] = $getters;
        }
        return $this->classPropertyGetters[$className];
    }

    /**
     * @param $object
     * @return array
     */
    private function serializeObject($object) {
        $properties = $this->getClassPropertyGetters($object);
        $data = array();
        foreach ($properties as $name => $property)
        {
            $data[$name] = $this->serializeInternal($object->$property());
        }
        return $data;
    }

    /**
     * @param $array
     * @return array
     */
    private function serializeArray($array)
    {
        $result = array();
        foreach ($array as $key => $value) {
            $result[$key] = $this->serializeInternal($value);
        }
        return $result;
    }  
} 

1
Şu an sana çok aşığım! Sana biraz domuz pastırması veya bira veya kek göndereceğim, peki ya kek?
Jonathan dos Santos

bu harika bir Sınıf! aynı zamanda korumalı nesne öğeleriyle de çalışır.
Roelof Berkepeis


2

Nesne türünüz özel olduğu için, çözümünüze katılma eğilimindeyim - bir kodlama yöntemi kullanarak (JSON veya içeriği serileştirme gibi) onu daha küçük parçalara ayırın ve diğer tarafta nesneyi yeniden oluşturmak için karşılık gelen koda sahip olun.


2

Benim versiyonum:

json_encode(self::toArray($ob))

Uygulama:

private static function toArray($object) {
    $reflectionClass = new \ReflectionClass($object);

    $properties = $reflectionClass->getProperties();

    $array = [];
    foreach ($properties as $property) {
        $property->setAccessible(true);
        $value = $property->getValue($object);
        if (is_object($value)) {
            $array[$property->getName()] = self::toArray($value);
        } else {
            $array[$property->getName()] = $value;
        }
    }
    return $array;
}

JsonUtils: GitHub


Tam olarak aradığım şey. Erlerle ilgili sorunu çözer. Basit ve küçük.
Fabian Picone

1

Bunu kullanmayı deneyin, bu benim için iyi çalıştı.

json_encode(unserialize(serialize($array)));

1

Değişken türlerinizi şu şekilde privatedeğiştirin:public

Bu basit ve daha okunaklı.

Örneğin

Çalışmıyor;

class A{
   private $var1="valuevar1";
   private $var2="valuevar2";
   public function tojson(){
    return json_encode($this)
   }
}

Çalışıyor;

class A{
   public $var1="valuevar1";
   public $var2="valuevar2";
   public function tojson(){
    return json_encode($this)
   }
}

çok tuhaf. ama gerçek bu.
Abilogos

0

Get yöntemleriyle bir nesneyi diziye dönüştüren güzel bir yardımcı sınıf yaptım. Özelliklere değil, sadece yöntemlere dayanır.

Bu nedenle, iki yöntem içeren aşağıdaki inceleme nesnesine sahibim:

gözden geçirmek

  • getAmountReviews: int
  • getReviews: yorum dizisi

Yorum Yap

  • getSubject
  • getDescription

Yazdığım betik, onu aşağıdaki gibi görünen özelliklere sahip bir diziye dönüştürecek:

    {
      amount_reviews: 21,
      reviews: [
        {
          subject: "In een woord top 1!",
          description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor."
        },
        {
          subject: "En een zwembad 2!",
          description: "Maecenas et aliquet mi, a interdum mauris. Donec in egestas sem. Sed feugiat commodo maximus. Pellentesque porta consectetur commodo. Duis at finibus urna."
        },
        {
          subject: "In een woord top 3!",
          description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor."
        },
        {
          subject: "En een zwembad 4!",
          description: "Maecenas et aliquet mi, a interdum mauris. Donec in egestas sem. Sed feugiat commodo maximus. Pellentesque porta consectetur commodo. Duis at finibus urna."
       },
       {
          subject: "In een woord top 5!",
          description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque laoreet lacus quis eros venenatis, sed tincidunt mi rhoncus. Aliquam ut pharetra diam, nec lobortis dolor."
    }
]}

Kaynak: Bir nesneyi JSON'a kodlanabilen bir diziye dönüştüren PHP Serializer.

Tek yapmanız gereken json_encode'u çıktının etrafına sarmaktır.

Komut dosyası hakkında bazı bilgiler:

  • Yalnızca get ile başlayan yöntemler eklenir
  • Özel yöntemler göz ardı edilir
  • Oluşturucu göz ardı edilir
  • Yöntem adındaki büyük karakterler, alt çizgi ve küçük harflerle değiştirilir

-7

Aynı problem üzerinde birkaç saat geçirdim. Dönüştürme nesnem, tanımlarına dokunmamam gereken (API) birçok başka nesneyi içeriyor, bu yüzden sanırım yavaş olabilecek bir çözüm buldum, ancak bunu geliştirme amacıyla kullanıyorum.

Bu, herhangi bir nesneyi diziye dönüştürür

function objToArr($o) {
$s = '<?php
class base {
    public static function __set_state($array) {
        return $array;
    }
}
function __autoload($class) {
    eval("class $class extends base {}");
}
$a = '.var_export($o,true).';
var_export($a);
';
$f = './tmp_'.uniqid().'.php';
file_put_contents($f,$s);
chmod($f,0755);
$r = eval('return '.shell_exec('php -f '.$f).';');
unlink($f);
return $r;
}

Bu, herhangi bir nesneyi stdClass'a dönüştürür

class base {
    public static function __set_state($array) {
        return (object)$array;
    }
}
function objToStd($o) {
$s = '<?php
class base {
    public static function __set_state($array) {
        $o = new self;
        foreach($array as $k => $v) $o->$k = $v;
        return $o;
    }
}
function __autoload($class) {
    eval("class $class extends base {}");
}
$a = '.var_export($o,true).';
var_export($a);
';
$f = './tmp_'.uniqid().'.php';
file_put_contents($f,$s);
chmod($f,0755);
$r = eval('return '.shell_exec('php -f '.$f).';');
unlink($f);
return $r;
}

Zaten kabul edilmiş başka bir ince ve doğru cevap var. Cevabınız kökten farklı, daha verimli veya kompakt bir şey katıyor mu? Sanırım hayır
Yaroslav

Dürüst olacağım; Bunun soruyu yanıtladığını sanmıyorum.
Dan Lugg

5
Yaklaşık 6 ay oldu; Olumlu oylar nedeniyle ve gelecekteki ziyaretçiler için bazı düzenlemeler yapmak için düzenli olarak buraya döndüm; Ben hala bunu yapmak gerekiyordu ne cehennem hiçbir fikrim yok.
Dan Lugg

unlink($thisAnswer);
Dan Lugg

1
inline php strings, eval, shell_exec(php)... cc-combo.
vp_arth
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.