将 PHP 对象序列化为 JSON

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6836592/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 01:25:16  来源:igfitidea点击:

Serializing PHP object to JSON

phpobjectjsonobject-serialization

提问by Dan Lugg

So I was wandering around php.netfor information about serializing PHP objects to JSON, when I stumbled across the new JsonSerializable Interface. It's only PHP >= 5.4though, and I'm running in a 5.3.x environment.

因此,当我偶然发现新的JsonSerializable Interface时,我在php.net 上徘徊以获取有关将 PHP 对象序列化为 JSON 的信息。不过,它只是PHP >= 5.4,而且我在 5.3.x 环境中运行。

How is this sort of functionality achieved PHP < 5.4?

PHP < 5.4是如何实现这种功能的?

I've not worked much with JSON yet, but I'm trying to support an API layer in an application, and dumping the data object (that would otherwise be sent to the view) into JSON would be perfect.

我还没有对 JSON 做过多少工作,但我正在尝试支持应用程序中的 API 层,将数据对象(否则将被发送到视图)转储到 JSON 中将是完美的。

If I attempt to serialize the object directly, it returns an empty JSON string; which is because I assume json_encode()doesn't know what the heck to do with the object. Should I recursively reduce the object into an array, and then encode that?

如果我尝试直接序列化对象,它会返回一个空的 JSON 字符串;这是因为我假设json_encode()不知道该对象有什么用。应予递归降低对象到一个数组,然后编码



Example

例子

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

echo json_encode($data)produces an empty object:

echo json_encode($data)产生一个空对象:

{}

var_dump($data)however, works as expected:

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


Addendum

附录

1)

1)

So this is the toArray()function I've devised for the Mf_Dataclass:

所以这是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;
}

However since the Mf_Dataobjects also have a reference to their parent (containing) object, this fails with recursion. Works like a charm though when I remove the _parentreference.

但是,由于Mf_Data对象也有对其父(包含)对象的引用,因此递归失败。虽然当我删除_parent引用时,它就像一个魅力。

2)

2)

Just to follow up, the final function to transform a complex tree-node object I went with was:

只是为了跟进,我使用的转换复杂树节点对象的最终函数是:

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

3)

I'm following up again, with a bit cleaner of an implementation. Using interfaces for an instanceofcheck seems much cleaner than method_exists()(however method_exists()does cross-cut inheritance/implementation).

我再次跟进,对实现进行了一些清理。使用接口进行instanceof检查似乎比method_exists()但是method_exists()横切继承/实现)更简洁

Using unset()seemed a bit messy too, and it seems that logic should be refactored into another method. However, this implementation doescopy the property array (due to array_diff_key), so something to consider.

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

}

采纳答案by Wrikken



edit: it's currently 2016-09-24, and PHP 5.4 has been released 2012-03-01, and support has ended2015-09-01. Still, this answer seems to gain upvotes. If you're still using PHP < 5.4, your are creating a security risk and endagering your project. If you have no compelling reasons to stay at <5.4, or even already use version >= 5.4, do not use this answer, and just use PHP>= 5.4 (or, you know, a recent one) and implement the JsonSerializable interface

编辑:目前是 2016 年 9 月 24 日,PHP 5.4 已于 2012 年 3 月 1 日发布,支持已于 2015 年 9 月 1 日结束。不过,这个答案似乎获得了赞成票。如果您仍在使用 PHP < 5.4,则会造成安全风险并危及您的项目。如果您没有令人信服的理由保持 <5.4,或者甚至已经使用版本 >= 5.4,请不要使用此答案,而只需使用 PHP>= 5.4(或者,您知道,最近的一个)并实现JsonSerializable 接口



You would define a function, for instance named getJsonData();, which would return either an array, stdClassobject, or some other object with visible parameters rather then private/protected ones, and do a json_encode($data->getJsonData());. In essence, implement the function from 5.4, but call it by hand.

您可以定义一个函数,例如 named getJsonData();,它会返回一个数组、stdClass对象或其他一些带有可见参数的对象,而不是私有/受保护的参数,然后执行json_encode($data->getJsonData());. 本质上是实现5.4的功能,不过是手动调用。

Something like this would work, as get_object_vars()is called from inside the class, having access to private/protected variables:

像这样的事情会起作用,就像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;
}

回答by takeshin

In the simplest cases type hinting should work:

在最简单的情况下,类型提示应该起作用:

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

回答by jfried

json_encode()will only encode public member variables. so if you want to include the private once you have to do it by yourself (as the others suggested)

json_encode()只会对公共成员变量进行编码。所以如果你想包括私人,一旦你必须自己做(正如其他人所建议的那样)

回答by Danny Yeshurun

Following code is doing the job using reflection. It assumes you have getters for the properties you want to serialize

以下代码使用反射来完成这项工作。它假设您有要序列化的属性的 getter

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

回答by webcodecs

Just implement an Interface given by PHP JsonSerializable.

只需实现 PHP JsonSerializable给出的接口。

回答by barfoon

Since your object type is custom, I would tend to agree with your solution - break it down into smaller segments using an encoding method (like JSON or serializing the content), and on the other end have corresponding code to re-construct the object.

由于您的对象类型是自定义的,我倾向于同意您的解决方案 - 使用编码方法(如 JSON 或序列化内容)将其分解为更小的部分,并在另一端使用相应的代码来重新构建对象。

回答by John Tribe

My version:

我的版本:

json_encode(self::toArray($ob))

Implementation:

执行:

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

JsonUtils : GitHub

回答by Navaneeth Mohan

Try using this, this worked fine for me.

尝试使用这个,这对我来说很好用。

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

回答by Ferhat KO?ER

Change to your variable types privateto public

将变量类型更改privatepublic

This is simple and more readable.

这很简单,也更易读。

For example

例如

Not Working;

不工作;

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

It is Working;

这是工作;

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

回答by Jamie

I made a nice helper class which converts an object with get methods to an array. It doesn't rely on properties, just methods.

我制作了一个很好的辅助类,它将带有 get 方法的对象转换为数组。它不依赖于属性,只依赖于方法。

So i have a the following review object which contain two methods:

所以我有一个包含两种方法的以下评论对象:

Review

  • getAmountReviews : int
  • getReviews : array of comments
  • getAmountReviews : 整数
  • getReviews : 评论数组

Comment

评论

  • getSubject
  • getDescription
  • 获取主题
  • 获取描述

The script I wrote will transform it into an array with properties what looks like this:

我编写的脚本会将其转换为一个具有如下属性的数组:

    {
      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 which converts an object to an array that can be encoded to JSON.

来源:PHP Serializer,它将对象转换为可以编码为 JSON 的数组。

All you have to do is wrap json_encode around the output.

您所要做的就是将 json_encode 包裹在输出周围。

Some information about the script:

关于脚本的一些信息:

  • Only methods which starts with get are added
  • Private methods are ignored
  • Constructor is ignored
  • Capital characters in the method name will be replaced with an underscore and lowercased character
  • 仅添加以 get 开头的方法
  • 私有方法被忽略
  • 构造函数被忽略
  • 方法名称中的大写字符将替换为下划线和小写字符