wake-up-neo.com

Serialisierung PHP Objekt zu JSON

Also habe ich mich in php.net umgesehen, um Informationen zum Serialisieren von PHP JSON-Objekten) zu erhalten, als ich über das neue gestolpert bin ) JsonSerializable Interface . Es ist nur PHP> = 5.4 , und ich laufe in einer 5.3.x-Umgebung.

Wie wird diese Art von Funktionalität erreicht PHP <5.4 ?

Ich habe noch nicht viel mit JSON gearbeitet, aber ich versuche, eine API-Ebene in einer Anwendung zu unterstützen und das Datenobjekt ( das ansonsten an die Ansicht gesendet würde) in JSON abzulegen es wäre perfekt.

Wenn ich versuche, das Objekt direkt zu serialisieren, gibt es eine leere JSON-Zeichenfolge zurück. Das liegt daran, dass ich nehme an, dass json_encode() nicht weiß, was zum Teufel mit dem Objekt zu tun ist. Soll ich das Objekt rekursiv in ein Array reduzieren und dann das codieren?


Beispiel

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

echo json_encode($data) erzeugt ein leeres Objekt:

{}

var_dump($data) funktioniert jedoch wie erwartet:

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

Nachtrag

1)

Das ist also die toArray() -Funktion, die ich für die Klasse Mf_Data Entwickelt habe:

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

Da die Mf_Data - Objekte jedoch auch einen Verweis auf ihr übergeordnetes ( enthaltend) Objekt haben, schlägt dies mit einer Rekursion fehl. Funktioniert jedoch wie ein Zauber, wenn ich den Verweis _parent Entferne.

2)

Die letzte Funktion zum Transformieren eines komplexen Baumknotenobjekts, mit dem ich gearbeitet habe, war:

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

Ich gehe noch einmal nach, mit einer etwas saubereren Implementierung. Die Verwendung von Interfaces für eine instanceof -Prüfung scheint weitaus sauberer zu sein als die Verwendung von method_exists() ( jedoch überschneidet method_exists() die Vererbung/Implementierung).

Die Verwendung von unset() schien ebenfalls etwas chaotisch zu sein, und es scheint, dass die Logik in eine andere Methode umgestaltet werden sollte. Diese Implementierung kopiert jedoch does das Eigenschaftsarray ( due to array_diff_key), also etwas zu beachten.

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

}
98
Dan Lugg

edit : aktuell ist es der 24.09.2016 und PHP 5.4 wurde am 01.03.2012 veröffentlicht, und Support hat beendet 01.09.2015. Dennoch scheint diese Antwort positive Stimmen zu bekommen. Wenn Sie immer noch PHP <5.4, Sie verursachen ein Sicherheitsrisiko und gefährden Ihr Projekt. Wenn Sie keine zwingenden Gründe haben, bei <5.4 zu bleiben, oder bereits Version> = 5.4 verwenden, verwenden Sie diese Antwort nicht und verwenden Sie einfach PHP> = 5.4 (oder, wie Sie wissen, eine neuere) und implementieren die JsonSerializable-Schnittstelle


Sie definieren eine Funktion mit dem Namen getJsonData();, die entweder ein Array, ein stdClass -Objekt oder ein anderes Objekt mit sichtbaren und nicht privaten/geschützten Parametern zurückgibt, und führen eine json_encode($data->getJsonData()); aus. Implementieren Sie im Wesentlichen die Funktion aus 5.4, rufen Sie sie jedoch manuell auf.

So etwas würde funktionieren, da get_object_vars() aus der Klasse heraus aufgerufen wird und Zugriff auf private/protected-Variablen hat:

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

In den einfachsten Fällen sollte der Tipp funktionieren:

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

json_encode() codiert nur öffentliche Membervariablen. Wenn Sie also das Private einschließen möchten, müssen Sie es selbst tun (wie die anderen vorgeschlagen haben).

19
jfried

Der folgende Code erledigt die Arbeit mit Reflektion. Es wird davon ausgegangen, dass Sie Getter für die Eigenschaften haben, die Sie serialisieren möchten

    <?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;
    }  
} 
8
Danny Yeshurun

Implementieren Sie einfach ein Interface, das von PHP JsonSerializable angegeben wird.

5
webcodecs

Da Ihr Objekttyp benutzerdefiniert ist, würde ich Ihrer Lösung eher zustimmen - zerlegen Sie ihn mithilfe einer Codierungsmethode (wie JSON oder Serialisierung des Inhalts) in kleinere Segmente und verfügen Sie am anderen Ende über entsprechenden Code, um das Objekt neu zu konstruieren.

2
barfoon

Meine Version:

json_encode(self::toArray($ob))

Implementierung:

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

1
John Tribe

Versuchen Sie es mit diesem, das hat gut funktioniert für mich.

json_encode(unserialize(serialize($array)));
1
Navaneeth Mohan

Ich habe eine Nice-Helfer-Klasse erstellt, die ein Objekt mit get-Methoden in ein Array konvertiert. Es beruht nicht auf Eigenschaften, sondern nur auf Methoden.

Ich habe also das folgende Review-Objekt, das zwei Methoden enthält:

Review

  • getAmountReviews: int
  • getReviews: Array von Kommentaren

Kommentar

  • getSubject
  • getDescription

Das Skript, das ich geschrieben habe, wird es in ein Array mit folgenden Eigenschaften umwandeln:

    {
      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."
    }
]}

Source: PHP Serializer, der ein Objekt in ein Array konvertiert, das in JSON codiert werden kann.

Alles was Sie tun müssen, ist json_encode um die Ausgabe zu wickeln.

Einige Informationen zum Skript:

  • Es werden nur Methoden hinzugefügt, die mit get beginnen
  • Private Methoden werden ignoriert
  • Konstruktor wird ignoriert
  • Großbuchstaben im Methodennamen werden durch Unterstriche und Kleinbuchstaben ersetzt
0
Jamie