内容简介:那么使用我们需要新建一个具有多种属性的对象
json_encode()
如何转化一个对象?
使用 json_encode()
将数组 array
转化成 json
字符串我们都已经很熟悉了
那么使用 json_encode()
转化一个对象是什么样的过程呢?
初步测试
我们需要新建一个具有多种属性的对象
新建 JsonTest
class JsonTest { public const TEST = 'c'; public $a = 'a'; public static $b = 'b'; protected $e = 'e'; private $d = 'd'; protected function test1() { return __FUNCTION__; } private function test2() { return __FUNCTION__; } public function test3() { return __FUNCTION__; } public static function test4() { return __FUNCTION__; } } 复制代码
执行
打印结果
echo json_encode(new JsonTest()); 复制代码
输出
{ "a": "a" } 复制代码
可以看得出,只有公开非静态的属性被打印出来了,其他东西(常量、私有变量、方法等等)都丢失了。
思考
在实际的应用中,多数情况下,我们的属性都是非公开的,但是我们又想在执行 json_encode()
的时候将它打印出来,该怎么办呢?
JsonSerializable
JsonSerializable是一个 PHP 的 JSON 序列化接口
官方定义
JsonSerializable { /* 方法 */ abstract public jsonSerialize ( void ) : mixed } 复制代码
可以看出 php
版本低于 5.4
是没有这个接口的
修改 JsonTest
继续测试
修改 JsonTest
让它实现 JsonSerializable
,并为其写一个 jsonSerialize
方法
class JsonTest implements JsonSerializable { public const TEST = 'c'; public $a = 'a'; public static $b = 'b'; protected $e = 'e'; private $d = 'd'; protected function test1() { return __FUNCTION__; } private function test2() { return __FUNCTION__; } public function test3() { return __FUNCTION__; } public static function test4() { return __FUNCTION__; } public function jsonSerialize() { $json = array(); foreach ($this as $key => $value) { $json[$key] = $value; } return $json; } } 复制代码
执行
打印结果
echo json_encode(new JsonTest()); 复制代码
输出
{ "a": "a", "e": "e", "d": "d" } 复制代码
可以看得出,公开属性和私有属性都被打印出来了,方法,常量以及静态变量没有打印出来(这是因为类( class
)中静态变量和常量的实现方式是所有对象共享的,并不具体属于某个类)
源码分析
这部分源码较多,我会按照源码中的 function
来一个一个进行分析,注意看代码块中的注释
里边对应有一些 option
的位运算,我先贴出来每个 option
常量对应的值, <<
是左移
/* json_encode() options */ #define PHP_JSON_HEX_TAG (1<<0) #define PHP_JSON_HEX_AMP (1<<1) #define PHP_JSON_HEX_APOS (1<<2) #define PHP_JSON_HEX_QUOT (1<<3) #define PHP_JSON_FORCE_OBJECT (1<<4) #define PHP_JSON_NUMERIC_CHECK (1<<5) #define PHP_JSON_UNESCAPED_SLASHES (1<<6) #define PHP_JSON_PRETTY_PRINT (1<<7) #define PHP_JSON_UNESCAPED_UNICODE (1<<8) #define PHP_JSON_PARTIAL_OUTPUT_ON_ERROR (1<<9) #define PHP_JSON_PRESERVE_ZERO_FRACTION (1<<10) #define PHP_JSON_UNESCAPED_LINE_TERMINATORS (1<<11) 复制代码
函数本身
PHP_JSON_API int php_json_encode(smart_str *buf, zval *val, int options) /* {{{ */ { return php_json_encode_ex(buf, val, options, JSON_G(encode_max_depth)); } PHP_JSON_API int php_json_encode_ex(smart_str *buf, zval *val, int options, zend_long depth) /* {{{ */ { php_json_encoder encoder; int return_code; // 初始化,开辟内存空间 php_json_encode_init(&encoder); encoder.max_depth = depth; // 真正用于编码的函数体 return_code = php_json_encode_zval(buf, val, options, &encoder); JSON_G(error_code) = encoder.error_code; return return_code; } /* }}} */ 复制代码
可以看出真正的编码函数是 php_json_encode_zval()
php_json_encode_zval()
smart_str_appendl()
是一个拼接字符串的函数,第三个参数是字符串的长度
buf
就是最终要返回的 json
字符串
int php_json_encode_zval(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */ { again: switch (Z_TYPE_P(val)) { case IS_NULL: smart_str_appendl(buf, "null", 4); break; case IS_TRUE: smart_str_appendl(buf, "true", 4); break; case IS_FALSE: smart_str_appendl(buf, "false", 5); break; case IS_LONG: smart_str_append_long(buf, Z_LVAL_P(val)); break; case IS_DOUBLE: if (php_json_is_valid_double(Z_DVAL_P(val))) { php_json_encode_double(buf, Z_DVAL_P(val), options); } else { encoder->error_code = PHP_JSON_ERROR_INF_OR_NAN; smart_str_appendc(buf, '0'); } break; case IS_STRING: return php_json_escape_string(buf, Z_STRVAL_P(val), Z_STRLEN_P(val), options, encoder); case IS_OBJECT: // 如果对象实现了JsonSerializable,就将对象中的jsonSerialize()返回的结果进行编码 if (instanceof_function(Z_OBJCE_P(val), php_json_serializable_ce)) { return php_json_encode_serializable_object(buf, val, options, encoder); } // 如果对象没有实现了JsonSerializable,就执行下边的这个php_json_encode_array() /* fallthrough -- Non-serializable object */ case IS_ARRAY: // 解析数组 return php_json_encode_array(buf, val, options, encoder); case IS_REFERENCE: //忽略引用 val = Z_REFVAL_P(val); goto again; default: encoder->error_code = PHP_JSON_ERROR_UNSUPPORTED_TYPE; if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) { smart_str_appendl(buf, "null", 4); } return FAILURE; } return SUCCESS; } /* }}} */ 复制代码
php_json_encode_array()
这个函数递归编码数组及没有实现 JsonSerializable()
的对象(只编码对象的公开属性)
static int php_json_encode_array(smart_str *buf, zval *val, int options, php_json_encoder *encoder) /* {{{ */ { int i, r, need_comma = 0; HashTable *myht; // r用来表示输出 `json` 的结构类型是数组还是对象 // 只有自然 排序 的数组(['a','b','c'])才有可能被输出为数组(考虑option可能为JSON_FORCE_OBJECT) if (Z_TYPE_P(val) == IS_ARRAY) { // 如果是数组 myht = Z_ARRVAL_P(val); // options中有JSON_FORCE_OBJECT 就强制输出对象,否则就判断数组是不是自然数组 r = (options & PHP_JSON_FORCE_OBJECT) ? PHP_JSON_OUTPUT_OBJECT : php_json_determine_array_type(val); } else { myht = Z_OBJPROP_P(val); //对象就是输出对象 r = PHP_JSON_OUTPUT_OBJECT; } if (myht && ZEND_HASH_GET_APPLY_COUNT(myht) > 0) { encoder->error_code = PHP_JSON_ERROR_RECURSION; smart_str_appendl(buf, "null", 4); return FAILURE; } PHP_JSON_HASH_APPLY_PROTECTION_INC(myht); if (r == PHP_JSON_OUTPUT_ARRAY) { //输出为数组 就用 [ 做开头 smart_str_appendc(buf, '['); } else { //输出为对象 就用 { 做开头 smart_str_appendc(buf, '{'); } // 当前递归的深度 ++encoder->depth; // zend_hash_num_elements 返回哈希表中元素的数量 i = myht ? zend_hash_num_elements(myht) : 0; if (i > 0) { zend_string *key; zval *data; zend_ulong index; //遍历当前维度的数组 如果当前元素不是数组 ZEND_HASH_FOREACH_KEY_VAL_IND(myht, index, key, data) { // ↓ begin 从这里开始都是判断key怎么处理以及元素末尾怎么处理 ↓↓↓↓ if (r == PHP_JSON_OUTPUT_ARRAY) { //need_comma初始值是0 if (need_comma) { smart_str_appendc(buf, ','); } else { need_comma = 1; } //这两个方法是option中有JSON_PRETTY_PRINT的时候才会执行的 php_json_pretty_print_char(buf, options, '\n'); php_json_pretty_print_indent(buf, options, encoder); } else if (r == PHP_JSON_OUTPUT_OBJECT) { if (key) { if (ZSTR_VAL(key)[0] == '\0' && ZSTR_LEN(key) > 0 && Z_TYPE_P(val) == IS_OBJECT) { //跳过受保护的属性和私有属性 /* Skip protected and private members. */ continue; } //need_comma初始值是0 if (need_comma) { smart_str_appendc(buf, ','); } else { need_comma = 1; } php_json_pretty_print_char(buf, options, '\n'); php_json_pretty_print_indent(buf, options, encoder); // 处理字符串属性的key(例如判断key中的中文或者特殊字符的处理) if (php_json_escape_string(buf, ZSTR_VAL(key), ZSTR_LEN(key), options & ~PHP_JSON_NUMERIC_CHECK, encoder) == FAILURE && (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR) && buf->s) { ZSTR_LEN(buf->s) -= 4; smart_str_appendl(buf, "\"\"", 2); } } else { if (need_comma) { smart_str_appendc(buf, ','); } else { need_comma = 1; } php_json_pretty_print_char(buf, options, '\n'); php_json_pretty_print_indent(buf, options, encoder); smart_str_appendc(buf, '"'); smart_str_append_long(buf, (zend_long) index); smart_str_appendc(buf, '"'); } smart_str_appendc(buf, ':'); php_json_pretty_print_char(buf, options, ' '); } // ↑ end 从这里之前都是判断key怎么处理以及元素末尾怎么处理 ↑↑↑↑ //继续调用对普通元素编码的 php_json_encode_zval() (实现数组和对象的递归闭环) if (php_json_encode_zval(buf, data, options, encoder) == FAILURE && !(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); return FAILURE; } } ZEND_HASH_FOREACH_END(); } PHP_JSON_HASH_APPLY_PROTECTION_DEC(myht); // 当前深度是否到达了设定的最大深度(默认512) if (encoder->depth > encoder->max_depth) { encoder->error_code = PHP_JSON_ERROR_DEPTH; if (!(options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR)) { return FAILURE; } } --encoder->depth; /* Only keep closing bracket on same line for empty arrays/objects */ if (need_comma) { php_json_pretty_print_char(buf, options, '\n'); php_json_pretty_print_indent(buf, options, encoder); } if (r == PHP_JSON_OUTPUT_ARRAY) { smart_str_appendc(buf, ']'); } else { smart_str_appendc(buf, '}'); } return SUCCESS; } /* }}} */ 复制代码
分析
看了源码,我得出了一些结论。
- 只有 null,布尔值,浮点数,整数,字符串才会被直接编码
-
对象要么实现
JsonSerializable
并定义一个jsonSerialize()
,要么就被当成一个数组,只会被处理公开非静态属性 -
json_encode()
并不会直接编码 数组 和 对象 ,而是会递归遍历出所有可遍历的元素,并处理 key -
源码中
php_json_encode_zval()
和php_json_encode_array()
的相互调用,实现了数组和对象遍历的闭环 - 引用不会被编码
另外,关于 json_encode()
的 options
,我觉得这里处理的技巧非常有趣,巧妙利用位运算来区别多个常量,有兴趣的慢慢看看研究研究。(提示,将 options
每个常量转成二进制来看, json_encode()
接受多个 option
是按位或 |
)
Demo
>>> $a = [1,2,3,4]; => [ 1, 2, 3, 4, ] >>> json_encode($a); => "[1,2,3,4]" >>> json_encode((object)$a); => "{"0":1,"1":2,"2":3,"3":4}" >>> json_encode($a,JSON_FORCE_OBJECT); => "{"0":1,"1":2,"2":3,"3":4}" >>> json_encode($a,JSON_FORCE_OBJECT|JSON_PRETTY_PRINT); => """ {\n "0": 1,\n "1": 2,\n "2": 3,\n "3": 4\n } """ 复制代码
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- js的类型转化三两事儿
- archTIS:将数据安全转化为经济增长
- 如何将JavaScript转化成Swift?(一)
- python3 第十章 - 如何进行进制转化
- 用Golang将图片转化成ASCII码
- 记一次bug解决过程(数字转化成中文)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Fluent Python
Luciano Ramalho / O'Reilly Media / 2015-8-20 / USD 39.99
Learn how to write idiomatic, effective Python code by leveraging its best features. Python's simplicity quickly lets you become productive with it, but this often means you aren’t using everything th......一起来看看 《Fluent Python》 这本书的介绍吧!