JSON पर PHP ऑब्जेक्ट को सीरियल करना


101

इसलिए मैं PHP को ऑब्जेक्ट्स के बारे में जानकारी के लिए php.net के आसपास भटक रहा था , जब मैंने JSON को नए JsonSerializable इंटरफ़ेस में ठोकर खाई, तो PHP ऑब्जेक्ट्स को क्रमबद्ध करने के बारे में जानकारी के लिए । यह केवल PHP> = 5.4 है, और मैं 5.3.x वातावरण में चल रहा हूं।

इस प्रकार की कार्यक्षमता PHP <5.4 को कैसे प्राप्त की जाती है ?

मैंने अभी तक JSON के साथ ज्यादा काम नहीं किया है, लेकिन मैं एक एप्लिकेशन में एक एपीआई परत का समर्थन करने की कोशिश कर रहा हूं, और JSON में डेटा ऑब्जेक्ट ( जो अन्यथा दृश्य में भेजा जाएगा ) को डंप करना सही होगा।

अगर मैं सीधे ऑब्जेक्ट को क्रमबद्ध करने का प्रयास करता हूं, तो यह एक खाली JSON स्ट्रिंग देता है; ऐसा इसलिए है क्योंकि मुझे लगता json_encode()है कि वस्तु के साथ क्या करना है पता नहीं है। क्या मुझे पुन: किसी ऑब्जेक्ट को सरणी में कम करना चाहिए, और फिर उसे एनकोड करना चाहिए ?


उदाहरण

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

echo json_encode($data) एक खाली वस्तु पैदा करता है:

{}

var_dump($data) हालाँकि, उम्मीद के मुताबिक काम करता है:

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

परिशिष्ट

1)

तो यह वह toArray()कार्य है जो मैंने Mf_Dataकक्षा के लिए तैयार किया है :

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

हालाँकि, चूंकि Mf_Dataवस्तुओं में उनके माता-पिता ( युक्त ) वस्तु का भी संदर्भ होता है , इसलिए यह पुनरावृत्ति के साथ विफल हो जाता है। जब मैं _parentसंदर्भ हटाता हूं तो एक आकर्षण की तरह काम करता है ।

2)

बस का पालन करने के लिए, एक जटिल ट्री-नोड ऑब्जेक्ट को बदलने के लिए अंतिम कार्य जो मैंने किया था:

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

मैं एक कार्यान्वयन के एक बिट क्लीनर के साथ फिर से पालन कर रहा हूं। एक instanceofचेक के लिए इंटरफेस का उपयोग करने से अधिक क्लीनर लगता है method_exists()( हालांकि method_exists()क्रॉस-कट इनहेरिटेंस / कार्यान्वयन )।

उपयोग करना unset()थोड़ा गड़बड़ भी लग रहा था, और ऐसा लगता है कि तर्क को दूसरी विधि में फिर से बनाया जाना चाहिए। हालांकि, यह कार्यान्वयन संपत्ति सरणी ( कारण ) की प्रतिलिपि बनाता है , इसलिए कुछ पर विचार करें।array_diff_key

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 अच्छा प्रश्न, इस सुविधा को अभी तक नहीं जानता था।
ताकेशिन

@takeshin - Yeop, डॉक्टर पृष्ठ पर दिनांक को 4 दिन पहले संपादित करें। मुझे यह देखकर खुशी हुई!
डैन लुग जुग

2
इसे देखने वाले अन्य लोगों के संदर्भ के लिए, json_encode वस्तुओं को ठीक से संभाल सकता है। हालाँकि, यह केवल उस ऑब्जेक्ट के सार्वजनिक-सदस्यों को एन्कोड करता है। तो अगर आपने संरक्षित या निजी वर्ग चर बनाए हैं, तो आपको पोस्ट किए गए तरीकों में से एक या JsonSerializable की आवश्यकता है।
मैथ्यू हर्बस्ट

@MatthewHerbst निश्चित रूप से। पुराना प्रश्न अब पुराना है, और <5.4 वास्तव में वैसे भी अब कोई विकल्प नहीं है (या कम से कम नहीं होना चाहिए) निश्चित रूप सेJsonSerializable
Dan Lugg

जवाबों:


45

संपादित करें : यह वर्तमान में 2016-09-24 है, और PHP 5.4 को 2012-03-01 जारी किया गया है, और समर्थन 2015-09-01 समाप्त हो गया है। फिर भी, इस उत्तर से लाभ प्राप्त होता है। यदि आप अभी भी PHP <5.4 का उपयोग कर रहे हैं, तो आप एक सुरक्षा जोखिम पैदा कर रहे हैं और अपनी परियोजना को खतरे में डाल रहे हैं । यदि आपके पास <5.4 पर रहने के लिए कोई बाध्यकारी कारण नहीं हैं, या यहां तक ​​कि पहले से ही संस्करण का उपयोग करें = = 5.4, तो इस उत्तर का उपयोग न करें , और बस PHP> = 5.4 का उपयोग करें (या, आप जानते हैं, हाल ही में एक) और JsonSerializable इंटरफ़ेस को लागू करें


उदाहरण के लिए, आप एक फ़ंक्शन को परिभाषित करेंगे getJsonData();, जो या तो एक सरणी, stdClassऑब्जेक्ट या किसी अन्य ऑब्जेक्ट को दृश्यमान मापदंडों के साथ वापस लौटाएगा, बजाय निजी / संरक्षित वाले, और एक ए json_encode($data->getJsonData());। संक्षेप में, फ़ंक्शन को 5.4 से लागू करें, लेकिन इसे हाथ से बुलाएं।

कुछ इस तरह काम करेगा, जैसा get_object_vars()कि कक्षा के अंदर से कहा जाता है, निजी / संरक्षित चर तक पहुंच:

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
धन्यवाद @Wrikken - क्या किसी ऑब्जेक्ट को कम करने के लिए कोई शॉर्टकट है, जिसमें सम्‍मिलित सरणी में कोई वस्तु ( दृश्यता या प्रकार की परवाह किए बिना सभी सदस्य हैं ), या इसे टाइपकास्ट कर रहे हैं stdClass? मैं परावर्तन की दिशा में सोच रहा हूं , लेकिन यदि नहीं, तो मैं इसे पुनरावृत्ति करने के लिए कुछ का पता लगाऊंगा।
डैन लुग जुग

प्रतिबिंब लंबा रास्ता होगा। जैसा कि आप अपने फ़ंक्शन में कक्षा के अंदर हैं getJsonData(), आप बस कॉल कर सकते हैं get_object_vars(), और उस परिणाम के माध्यम से लूप कर सकते हैं जो अधिक वस्तुओं की तलाश कर रहा है।
जूल

मैंने लगभग इसे हल कर लिया है; अब समस्या पुनरावृत्ति है। प्रत्येक वस्तु में एक _parentगुण होता है इसलिए पेड़ को जड़ से उखाड़ा जा सकता है। अपडेट के लिए मेरा संपादन देखें; शायद मुझे एक और सवाल पूछना चाहिए क्योंकि यह मुद्दा अब मेरे मूल से अलग है।
डैन लुग जुग

unset($array['_parent']);टहलने से पहले एक सरल उपाय करना चाहिए।
२१'११ तक वरिकेन

बहुत बढ़िया, धन्यवाद @Wrikken - मैं $parentउपयोगकर्ता के डेटा के रूप में एक संदर्भ वस्तु को पारित करते हुए, जटिल समानता परीक्षणों की कोशिश करना शुरू कर रहा था array_walk_recursive()। सादगी खुबसुरत है! इसके अलावा, इसकी $array["\0class\0property"]वजह से अशक्त प्रदूषण क्योंकि मैं कास्टिंग का उपयोग कर रहा था। मुझे लगता है कि मैं स्विच करूँगा get_object_vars()
डैन लुग जूल

91

सरलतम मामलों में टाइपिंग का काम करना चाहिए:

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

7
यदि आप नामस्थान और ऑटो लोडर के साथ काम करते हैं तो यह लंबे समय तक घुमावदार / बदसूरत संपत्ति के नाम देता है।
बीटा डिड

यह सबसे अच्छा समाधान है, सटीक और संक्षिप्त!
सुजल मंडल

4
क्या क्लीनर संपत्ति के नाम प्राप्त करने का एक तरीका है?
क्रिस्टोफर

5
यह प्रोप नामों के प्रारंभ में \ u0000 * \ u0000 क्यों जोड़ता है?
एलिया वीस

1
निजी संपत्तियों के साथ बेकार। आप सभी को en.wikipedia.org/wiki/Open/closed_principle के बारे में सीखना चाहिए ।
फेबियन पॉनिक

19

json_encode()केवल सार्वजनिक सदस्य चर को सांकेतिक शब्दों में बदलना होगा। इसलिए यदि आप निजी को एक बार खुद से शामिल करना चाहते हैं (जैसा कि दूसरों ने सुझाव दिया है)


8

निम्नलिखित कोड प्रतिबिंब का उपयोग कर काम कर रहा है। यह मानता है कि आपके पास गुण हैं जिन्हें आप क्रमबद्ध करना चाहते हैं

    <?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
मैं अभी तुम्हारे साथ प्यार में हूँ! मैं आपको कुछ बेकन या बीयर या एक कपकेक भेजूंगा जो कपकेक के बारे में है?
जोनाथन डॉस सैंटोस

यह एक महान वर्ग है! यह संरक्षित वस्तु वस्तुओं के साथ भी काम करता है।
Roelof Berkepeis


2

चूंकि आपका ऑब्जेक्ट प्रकार कस्टम है, इसलिए मैं आपके समाधान से सहमत होना चाहूंगा - एन्कोडिंग विधि (जैसे JSON या सामग्री को क्रमबद्ध करना) का उपयोग करके इसे छोटे खंडों में तोड़ दूंगा, और दूसरे छोर पर ऑब्जेक्ट को फिर से बनाने के लिए संबंधित कोड है।


2

मेरा संस्करण:

json_encode(self::toArray($ob))

कार्यान्वयन:

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

इस का उपयोग करने की कोशिश करो, यह मेरे लिए ठीक काम किया।

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

1

अपने चर प्रकार के लिए बदलें privateकरने के लिएpublic

यह सरल और अधिक पठनीय है।

उदाहरण के लिए

काम नहीं कर रहा;

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

यह काम कर रहा है;

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

यह बहुत अजीब है। पर यही सच है।
अबिलोगोस

0

मैंने एक अच्छा सहायक वर्ग बनाया, जो किसी ऑब्जेक्ट को एक सरणी में तरीकों के साथ परिवर्तित करता है। यह गुणों, सिर्फ तरीकों पर निर्भर नहीं करता है।

इसलिए मेरे पास निम्नलिखित समीक्षा वस्तु है जिसमें दो विधियाँ हैं:

समीक्षा

  • getAmountReviews: int
  • getReviews: टिप्पणियों की सरणी

टिप्पणी

  • getSubject
  • getDescription

मैंने जो पटकथा लिखी है, वह एक सरणी में गुणों के साथ बदल जाएगी जो इस तरह दिखती है:

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

स्रोत: PHP धारावाहिक जो एक वस्तु को एक सरणी में परिवर्तित करता है जिसे JSON में एन्कोड किया जा सकता है।

आपको बस आउटपुट के चारों ओर json_encode लपेटना है।

स्क्रिप्ट के बारे में कुछ जानकारी:

  • केवल विधियाँ जो आरंभ होती हैं, जोड़ दी जाती हैं
  • निजी तरीकों की अनदेखी की जाती है
  • कंस्ट्रक्टर को नजरअंदाज किया जाता है
  • विधि नाम के कैपिटल कैरेक्टर को अंडरस्कोर और लोअरकेड कैरेक्टर से बदला जाएगा

-7

मैंने उसी समस्या पर कुछ घंटे बिताए। मेरी वस्तु को रूपांतरित करने के लिए कई अन्य हैं जिनकी परिभाषाएँ मैं स्पर्श (एपीआई) नहीं कर रहा हूँ, इसलिए मैं एक ऐसा समाधान लेकर आया हूँ जिसका अनुमान मुझे धीमा हो सकता है, लेकिन मैं इसका उपयोग विकास के उद्देश्य से कर रहा हूँ।

यह किसी भी ऑब्जेक्ट को सरणी में कनवर्ट करता है

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

यह किसी भी ऑब्जेक्ट को stdClass में कनवर्ट करता है

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

पहले से स्वीकृत एक और बढ़िया और सटीक उत्तर है। क्या आपका जवाब कुछ अलग, अधिक कुशल या कॉम्पैक्ट जोड़ता है? मुझे नहीं लगता
यारोस्लाव

मैं ईमानदार होने जा रहा हूँ; मुझे नहीं लगता कि यह सवाल का जवाब देता है, बिल्कुल।
डैन लुग

5
लगभग 6 महीने हो गए; मैं समय-समय पर upvotes के कारण यहां लौटा हूं, और भविष्य के आगंतुकों के लिए कुछ संपादन करने के लिए; मुझे अभी भी पता नहीं है कि यह नरक क्या करना चाहिए है।
डैन लैग

unlink($thisAnswer);
डेन लुग

लोगों को यह समझ में नहीं आता कि वे क्या करते हैं। यह एक सटीक समाधान के अनुसार नहीं हो सकता है, लेकिन यह देखने के लिए कुछ है। ऐसे मामले में आप डाउनवोट से पहले स्पष्टीकरण मांगते हैं।
गामली
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.