当前位置: 首页 > 后端技术 > PHP

深入学习PHP中JSON相关函数

时间:2023-03-29 23:16:49 PHP

深入学习PHP中JSON相关函数在我们刚上班的年代,还是XML为主的世界,但是现在JSON数据格式已经有了成为各种应用传输的事实标准。近几年开始学习编程和开发的同学可能从来没有接触过使用XML进行数据传输。当然,时代总是在进步的。与XML相比,JSON更方便、更快速、更具可读性。但实际上,从语义上看,XML的表现力更强。话不多说,在PHP中操作JSON其实很简单。最常用的函数是json_encode()和json_decode()。他们有一些警告和一些乐趣。今天,让我们深入了解一下。JSONEncoding首先,我们准备一个数组,用于我们后续的编码操作。$data=['id'=>1,'name'=>'testsituation','cat'=>['student&"job"',],'number'=>"123123123",'edu'=>[['姓名'=>'中学','日期'=>'2015-2018',],['姓名'=>'大学','日期'=>'2018-2022',],],];很简单的数组,其实也没什么特别的,就是嵌套数据,一些中文和特殊符号。对于常见的JSON编码,直接使用json_encode()即可。$json1=json_encode($data);var_dump($json1);//string(215)"{"id":1,"name":"\u6d4b\u8bd5\u60c5\u51b5","cat":["\u5b66\u751f&\"\u5728\u804c\""],"number":"123123123","edu":[{"name":"\u4e2d\u5b66<\/b>","date":"2015-2018"},{"name":"\u5927\u5b66<\/b>","date":"2018-2022"}]}"上述编码JSON的中文处理你有没有发现数据有什么问题?没错,相信很多人一眼就能看出,所有的汉字都被转成\uxxxx格式了。这其实是默认的,json_encode()函数会把这些多字节字符转换成Unicode格式。我们可以通过直接在json_encode()后面加一个常量参数来解决这个问题,这样就可以正常显示汉字了。$json1=json_encode($data,JSON_UNESCAPED_UNICODE);var_dump($json1);//string(179)"{"id":1,"name":"测试情况","cat":["Student&\"Active\""],"number":"123123123","edu":[{"name":"中学<\/b>","date":"2015-2018"},{"name":"University<\/b>","date":"2018-2022"}]}"当然,这太无聊了。因为我在面试的时候,面试官问我不使用这个常量参数能不能解决这种问题。你可以忽略下面的代码并考虑你自己的解决方案吗?函数t($data){foreach($dataas$k=>$d){if(is_object($d)){$d=(array)$d;}if(is_array($d)){$data[$k]=t($d);}else{$data[$k]=urlencode($d);}}return$data;}$newData=t($data);$json1=json_encode($newData);var_dump(urldecode($json1));//string(177)"{"id":"1","name":"考试情况","cat":["Student&"Working""],"number":"123123123","edu":[{"name":"MiddleSchool","date":"2015-2018"},{"name":"大学","date":"2018-2022"}]}"其实是一个很简单的方案,递归将数据中所有字段内容转成urlencode()编码,再用json_encode()编码,完成后再用urldecode()解密。有趣吗?其实这是很多老程序员的把戏,因为常量JSON_UNESCAPED_UNICODE只有在PHP5.4之后才有。如果想让编码后的数据直接显示中文,只能这样做了。当然现在已经是PHP8的时代了,不需要这么麻烦的操作,但是也不排除有一些面试厅因为是老coder,所以故意问这样的问题。谁都明白,知道有这么个东西就够了。毕竟在实际项目开发中,可能很少有系统使用PHP5.4以下的版本(这样的公司无所谓,技术更新太慢了)。除了JSON_UNESCAPED_UNICODE其他参数之外,我们还有很多常量参数可以使用,而且这个参数是可以并行操作的,也就是多个常量参数可以一起工作。$json1=json_encode($data,JSON_UNESCAPED_UNICODE|JSON_HEX_TAG|JSON_HEX_AMP|JSON_NUMERIC_CHECK|JSON_HEX_QUOT);var_dump($json1);//string(230)"{"id":1,"name":"测试用例","cat":["Student\u0026\u0022Working\u0022"],"number":123123123"edu":[{"name":"\u003Cb\u003EMiddleSchool\u003C\/b\u003E","date":"2015-2018"},{"name":"\u003Cb\u003EUniversity\u003C\/b\u003E","date":"2018-2022"}]}"这一堆参数其实是针对我们数据中的一些特殊符号,比如&符号,<>HTML标签等。当然还有仍然是一些常量参数没有完全展示出来。大家可以自行参考官方手册中的描述。另外json_encode()还有第三个参数,代表迭代层级。比如上面的数据是一个三层的多维数组,那么我们至少要给3才能正常解析。我们只是在下面的代码中给了一个1,所以返回的内容是false。即不能编码成功。默认情况下,此参数的值为512。var_dump(json_encode($data,JSON_UNESCAPED_UNICODE,1));//bool(false)对象及格式处理默认json_encode()会根据数据的类型进行编码,所以如果是数组,那么其编码后的内容就是JSON的数组格式。这个时候我们也可以加一个JSON_FORCE_OBJECT让它把一个数组编码成对象的形式。$data=[];var_dump(json_encode($data));//string(2)"[]"var_dump(json_encode($data,JSON_FORCE_OBJECT));//string(2)"{}"beforemath我们了解了相关函数。如果数据中有NAN之类的数据,则json_encode()无法编码。事实上,我们可以添加一个JSON_PARTIAL_OUTPUT_ON_ERROR来替换一些不可编码的值。在下面的代码中,我们可以用它来将NAN替换为0。$data=NAN;var_dump(json_encode($data));//bool(false)var_dump(json_encode($data,JSON_PARTIAL_OUTPUT_ON_ERROR));//对象编码的0属性问题对于对象来说,JSON编码后的内容就像序列化一样,只会有对象的属性,没有方法。毕竟JSON最大的用途就是数据传输,该方法对数据传输没有实际作用。属性也会根据封装的不同而不同,只有public,即公共属性才会被编码。$data=新类{private$a=1;保护$b=2;公共$c=3;公共函数x(){}};var_dump(json_encode($data));//string(7)"{"c":3}"从这段测试代码可以看出,protected,privateproperties,andthatmethod不会被编码。JSON解码其实JSON解码更简单,因为json_decode()没有那么多常量参数。var_dump(json_decode($json1));//object(stdClass)#1(5){//["id"]=>//int(1)//["name"]=>//string(12)"测试用例"//["cat"]=>//...//...var_dump(json_decode($json1,true));//array(5){//["id"]=>//int(1)//["name"]=>//string(12)"测试情况"//["cat"]=>//...//...首先我们来看它的第二个参数。这个参数的作用其实可以从代码中看出。如果该参数不填,即默认为false,则解码后的数据为object格式。如果我们将此参数设置为true,则解码结果将是数组格式。这也是一个很常用的函数,就不多解释了。var_dump(json_decode('{"a":1321231231231231231231231231231231231231231231231231231231231231231231233}',true));//array(1){//["a"]=>//float(1.3212312312312E+72)//}var_dump(json_decode('{"a":1321231231231231231231231231231231231231231231231231231231231231231231233}',true,512,JSON_BIGINT_AS_STRING));//array(1){//["a"]=>//string(73)"1321231231231231231231231231231231231231231231231231231231231231231231233"//}对于这种非常对于longnumeric格式的数据,如果直接通过json_decode()解码,会直接转化为科学计数法。我们在解码的时候可以直接使用一个JSON_BIGINT_AS_STRING常量参数,直接把这个数据转成字符串,其实就是保留了数据本来的样子。注意这里的json_decode()函数的参数因为是把对象转成数组的参数所以有四个参数。第三个参数是迭代深度,第四个是定义这些格式化常量值。而且是json_encode()的逆向,迭代深度参数在前,格式常量参数在后,这里一定要注意!如果数据错误,则json_decode()将返回NULL。var_dump(json_decode("",true));//NULLvar_dump(json_decode("{a:1}",true));//NULL错误处理在上面两段代码中我们已经演示了如果编码或者解码后的数据有问题会发生什么,比如json_encode()会返回false,json_decode()会返回NULL。但具体原因是什么?$data=NAN;var_dump(json_encode($data));//bool(false)var_dump(json_last_error());//int(7)var_dump(json_last_error_msg());//string(34)"InfandNaNcannotbeJSONencoded"是的,json_last_error()和json_last_error_msg()在JSON操作期间返回错误信息。也就是说json_encode()和json_decode()在正常情况下是不会报错的。如果我们想要获取错误信息,就必须使用这两个函数来获取。这也是很多新手同学没有注意到的地方。没有报错信息,也没有抛出异常,其实对我们的开发调试是很不友好的。因为很有可能找了半天也不知道问题出在哪里。PHP7.3之后新增了一个常量参数,可以让我们的json_encode()和json_decode()在编码解码错误的时候抛出异常,方便我们快速定位问题。现在如果你的系统运行环境是PHP7.3以上的话,强烈推荐使用这个常量参数让系统抛出异常。//php7.3var_dump(json_encode($data,JSON_THROW_ON_ERROR));//致命错误:未捕获的JsonException:Inf和NaN不能被JSON编码var_dump(json_decode('',true,512,JSON_THROW_ON_ERROR));//PHP致命错误:UncaughtJsonException:SyntaxerrorJSON_THROW_ON_ERROR对json_encode()和json_decode()都有效。同样的,只要设置了这个常量参数,我们就可以用try...catch来捕获。尝试{var_dump(json_encode($data,JSON_THROW_ON_ERROR));}catch(JsonException$e){var_dump($e->getMessage());//string(34)"Inf和NaN不能进行JSON编码"}JSON序列Serializable接口在上一篇文章中,我们学习了使用Serializable接口来自定义PHP类的序列化。也就是说,通过Serializable接口,我们可以自定义序列化格式的内容。对于JSON,它也提供了一个JsonSerializable接口来实现我自定义JSON编码时的对象格式内容。类jsontest实现JsonSerializable{publicfunction__construct($value){$this->value=$value;}publicfunctionjsonSerialize(){return$this->value;}}print"Null->".json_encode(newjsontest(无效的))。"\n";打印"数组->"。json_encode(新的jsontest(数组(1,2,3)))。"\n";print"Assoc.->".json_encode(newjsontest(array('a'=>1,'b'=>3,'c'=>4)))。"\n";打印"Int->"。json_encode(新的jsontest(5))。"\n";print"String->".json_encode(newjsontest('Hello,World!'))。"\n";print"Object->".json_encode(newjsontest((object)array('a'=>1,'b'=>3,'c'=>4)))。"\n";//Null->null//数组->[1,2,3]//Assoc。->{"a":1,"b":3,"c":4}//整数->5//字符串->"Hello,World!"//对象->{"a":1,"b":3,"c":4}这是一个小例子,只需要实现JsonSerializable接口中的jsonSerialize()方法,返回内容指定jsontest对象的JSON编码格式即可。这里我们只是简单的返回了数据的内容,和普通的json_encode()没有太大区别。让我们来看一个复杂的例子。类Student实现JsonSerializable{private$id;私人的名字;私人$猫;私人$号码;私人$edu;公共函数__construct($id,$name,$cat=null,$number=null,$edu=null){$this->id=$id;$this->name=$name;$this->cat=$cat;$this->number=$number;$this->edu=$edu;}publicfunctionjsonSerialize(){if(!$cat){$this->cat=['学生'];}if(!$edu){$this->edu=newstdClass;}$this->number='学号:'.(!$number?mt_rand():$number);如果($this->id==2){return[$this->id,$this->name,$this->cat,$this->number,$this->edu,];}返回['id'=>$this->id,'name'=>$this->name,'cat'=>;$this->cat,'number'=>$this->number,'edu'=>$this->edu,];}}var_dump(json_encode(newStudent(1,'Test1'),JSON_UNESCAPED_UNICODE));//string(82)"{"id":1,"name":"Test1","cat":["student"],"number":"StudentID:14017495","edu":{}}"var_dump(json_encode([newStudent(1,'Test1'),newStudent(2,'Test2')],JSON_UNESCAPED_UNICODE));//string(137)"[{"id":1,"name":"测试1","cat":["Student"],"number":"学号:1713936069","edu":{}},[2,"Test2",["Student"],"学号:499173036",{}]]"在这个例子中,我们在jsonSerialize()如果数据不传值,比如null,会给出一个默认值。然后在id为2的情况下返回一个普通数组。第二条数据的格式可以看最后的注释。这个界面是不是很有意思?相信大家对上面的json_encode()和json_decode()可能已经很熟悉了,但是估计很多人都没有接触过这个接口。是不是很有趣?总结果然,凡事都怕深挖。如果你不学习它,你就不知道它。一旦你学会它,你会感到震惊。日常用起来这么简单的JSON操作相关的函数,其实还有很多我们不知道的好用的函数。当然,最重要的是看文档,理解并记住一些非常有用的常量参数。另外,抛出异常的功能也是本文的重点。建议已经到版本的朋友使用JSON_THROW_ON_ERROR让错误及时抛出,及时发现!测试代码:https://github.com/zhangyue0503/dev-blog/blob/master/php/202012/source/11。深入学习PHP中JSON相关的函数。PHP参考文档:https://www.php。net/manual/zh/book.json.php各媒体平台均可搜索【硬核项目经理】