PHP에서 JSON을 작동하는 방법은 무엇입니까? 이 기사에서는 PHP의 JSON 관련 기능에 대해 자세히 알아보고 이러한 기능을 사용할 때 주의해야 할 사항을 소개합니다. 도움이 되길 바랍니다.
우리가 처음 일을 시작하던 시대에는 여전히 XML이 세상을 지배했지만 이제는 JSON 데이터 형식이 다양한 애플리케이션 전송의 사실상 표준이 되었습니다. 최근 몇 년 동안 프로그래밍과 개발을 배우기 시작한 학생들은 데이터 전송에 XML을 사용하는 방법을 접해 본 적이 없을 것입니다. 물론, 시대는 지속적으로 발전하고 있습니다. XML에 비해 JSON은 더 편리하고 빠르며 읽기 쉽습니다. 그러나 실제로 의미론적 관점에서 보면 XML이 더 표현력이 뛰어납니다.
더 이상 고민하지 않고, PHP에서 JSON을 작동하는 것은 실제로 가장 일반적으로 사용되는 두 가지 함수는 json_encode()와 json_decode()입니다. 조심해야 할 것도 있고, 즐길 것도 있습니다. 오늘은 이에 대해 심도있게 공부해 보겠습니다.
먼저, 후속 인코딩 작업을 위한 배열을 준비합니다.
$data = [ 'id' => 1, 'name' => '测试情况', 'cat' => [ '学生 & "在职"', ], 'number' => "123123123", 'edu' => [ [ 'name' => '<b>中学</b>', 'date' => '2015-2018', ], [ 'name' => '<b>大学</b>', 'date' => '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":"<b>\u4e2d\u5b66<\/b>","date":"2015-2018"},{"name":"<b>\u5927\u5b66<\/b>","date":"2018-2022"}]}"
위의 인코딩된 JSON 데이터에서 문제를 발견하셨나요? 그렇죠, 많은 분들이 한눈에 한자를 모두 uxxxx 형식으로 변환한 것을 보실 수 있을 거라 믿습니다. 이는 실제로 기본적으로 json_encode() 함수가 이러한 멀티바이트 문자를 유니코드 형식 콘텐츠로 변환합니다. json_encode() 바로 뒤에 상수 매개변수를 추가하면 한자가 정상적으로 표시될 수 있도록 하여 이 문제를 해결할 수 있습니다.
$json1 = json_encode($data, JSON_UNESCAPED_UNICODE); var_dump($json1); // string(179) "{"id":1,"name":"测试情况","cat":["学生 & \"在职\""],"number":"123123123","edu":[{"name":"<b>中学<\/b>","date":"2015-2018"},{"name":"<b>大学<\/b>","date":"2018-2022"}]}"
그렇게 하면 당연히 지루할 텐데. 한번은 면접관이 인터뷰 중에 나에게 이 상수 매개변수를 사용하지 않고 이 문제를 해결할 수 있는지 물었기 때문입니다. 아래 코드를 보지 말고 자신만의 솔루션을 생각해 보세요.
function t($data) { foreach ($data as $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":["学生 & "在职""],"number":"123123123","edu":[{"name":"<b>中学</b>","date":"2015-2018"},{"name":"<b>大学</b>","date":"2018-2022"}]}"
는 실제로 매우 간단한 솔루션입니다. 데이터의 모든 필드 내용을 urlencode() 인코딩으로 재귀적으로 변환한 다음 json_encode()를 사용하여 인코딩하고, 완료 후 urldecode()를 사용하여 디코딩합니다. 흥미롭지 않나요? 실제로 JSON_UNESCAPED_UNICODE 상수는 PHP5.4 이후에만 사용할 수 있었기 때문에 이는 많은 오래된 프로그래머들의 작은 트릭이었습니다. 이전에는 인코딩된 데이터를 중국어로 직접 표시하려면 이 방법만 사용할 수 있었습니다.
물론 지금은 PHP8 시대라 그런 번거로운 작업은 필요 없습니다. 하지만 일부 면접관에서는 경험이 풍부한 코더이기 때문에 일부러 그런 질문을 하는 경우도 배제할 수 없습니다. 사실은 이것만 알아도 충분하다는 점 여러분 이해해 주시기 바랍니다. 결국 실제 프로젝트 개발에서는 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":["学生 \u0026 \u0022在职\u0022"],"number":123123123,"edu":[{"name":"\u003Cb\u003E中学\u003C\/b\u003E","date":"2015-2018"},{"name":"\u003Cb\u003E大学\u003C\/b\u003E","date":"2018-2022"}]}"
이 매개변수 묶음은 실제로 & 문자, <> HTML 태그 등과 같은 데이터의 일부 특수 기호에 사용됩니다. 물론, 아직 완전히 표시되지 않은 일부 상수 매개변수가 있습니다. 공식 매뉴얼에서 직접 지침을 확인할 수 있습니다.
또한 json_encode()에는 반복 수준을 나타내는 세 번째 매개변수가 있습니다. 예를 들어 위에 있는 데이터는 3개 레벨의 다차원 배열이므로 정상적으로 구문 분석하려면 최소 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) "{}"
우리는 이전에 수학 관련 함수에 대해 이야기할 때 데이터에 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의 가장 큰 용도는 데이터 전송이며, 방법은 데이터 전송에 실질적인 영향을 미치지 않습니다. 속성은 캡슐화에 따라 달라집니다. 공용 속성, 즉 공용 속성만 인코딩됩니다.
$data = new class { private $a = 1; protected $b = 2; public $c = 3; public function x(){ } }; var_dump(json_encode($data)); // string(7) "{"c":3}"
从这段测试代码中可以看出,protected 、 private 属性以及那个方法都不会被编码。
对于 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 ,那么解码出来的数据是对象格式的。而我们将这具参数设置为 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" // }
对于这种非常长的数字格式的数据来说,如果直接 json_decode() 解码的话,它会直接转换成 科学计数法 。我们可以直接使用一个 JSON_BIGINT_AS_STRING 常量参数,将这种数据在解码的时候直接转换成字符串,其实也就是保留了数据的原始样貌。注意,这里 json_decode() 函数的参数因为有那个转换对象为数组的参数存在,所以它有四个参数,第三个参数是迭代深度,第四个就是定义这些格式化常量值的。而且它和 json_encode() 是反过来的,迭代深度参数在前,格式常量参数在后面,这里一定要注意哦!
如果数据是错误的,那么 json_decode() 会返回 NULL 。
var_dump(json_decode("", true)); // NULL var_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) "Inf and NaN cannot be JSON encoded"
没错,json_last_error() 和 json_last_error_msg() 就是返回 JSON 操作时的错误信息的。也就是说,json_encode() 和 json_decode() 在正常情况下是不会报错的,我们如果要获得错误信息,就得使用这两个函数来获取。这一点也是不少新手小同学没有注意过的地方,没错误信息,不抛出异常问题对我们的开发调试其实是非常不友好的。因为很可能找了半天都不知道问题出在哪里。
在 PHP7.3 之后,新增加了一个常量参数,可以让我们的 json_encode() 和 json_decode() 在编解码错误的时候抛出异常,这样我们就可以快速地定位问题了,现在如果大家的系统运行环境是 PHP7.3 以上的话,非常推荐使用这个常量参数让系统来抛出异常。
// php7.3 var_dump(json_encode($data, JSON_THROW_ON_ERROR)); // Fatal error: Uncaught JsonException: Inf and NaN cannot be JSON encoded var_dump(json_decode('', true, 512, JSON_THROW_ON_ERROR)); // PHP Fatal error: Uncaught JsonException: Syntax error
JSON_THROW_ON_ERROR 是对 json_encode() 和 json_decode() 都起效的。同样,只要设定了这个常量参数,我们就可以使用 try...catch 来进行捕获了。
try { var_dump(json_encode($data, JSON_THROW_ON_ERROR)); } catch (JsonException $e) { var_dump($e->getMessage()); // string(34) "Inf and NaN cannot be JSON encoded" }
在之前的文章中,我们学习过 使用Serializable接口来自定义PHP中类的序列化 。也就是说,通过 Serializable 接口我们可以自定义序列化的格式内容。而对于 JSON 来说,同样也提供了一个 JsonSerializable 接口来实现我自定义 JSON 编码时的对象格式内容。
class jsontest implements JsonSerializable { public function __construct($value) {$this->value = $value;} public function jsonSerialize() {return $this->value;} } print "Null -> " . json_encode(new jsontest(null)) . "\n"; print "Array -> " . json_encode(new jsontest(array(1, 2, 3))) . "\n"; print "Assoc. -> " . json_encode(new jsontest(array('a' => 1, 'b' => 3, 'c' => 4))) . "\n"; print "Int -> " . json_encode(new jsontest(5)) . "\n"; print "String -> " . json_encode(new jsontest('Hello, World!')) . "\n"; print "Object -> " . json_encode(new jsontest((object) array('a' => 1, 'b' => 3, 'c' => 4))) . "\n"; // Null -> null // Array -> [1,2,3] // Assoc. -> {"a":1,"b":3,"c":4} // Int -> 5 // String -> "Hello, World!" // Object -> {"a":1,"b":3,"c":4}
这是一个小的示例,只需要实现 JsonSerializable 接口中的 jsonSerialize() 方法并返回内容就可以实现这个 jsontest 对象的 JSON 编码格式的指定。这里我们只是简单地返回了数据的内容,其实和普通的 json_encode() 没什么太大的区别。下面我们通过一个复杂的例子看一下。
class Student implements JsonSerializable { private $id; private $name; private $cat; private $number; private $edu; public function __construct($id, $name, $cat = null, $number = null, $edu = null) { $this->id = $id; $this->name = $name; $this->cat = $cat; $this->number = $number; $this->edu = $edu; } public function jsonSerialize() { if (!$cat) { $this->cat = ['学生']; } if (!$edu) { $this->edu = new stdClass; } $this->number = '学号:' . (!$number ? mt_rand() : $number); if ($this->id == 2) { return [ $this->id, $this->name, $this->cat, $this->number, $this->edu, ]; } return [ 'id' => $this->id, 'name' => $this->name, 'cat' => $this->cat, 'number' => $this->number, 'edu' => $this->edu, ]; } } var_dump(json_encode(new Student(1, '测试一'), JSON_UNESCAPED_UNICODE)); // string(82) "{"id":1,"name":"测试一","cat":["学生"],"number":"学号:14017495","edu":{}}" var_dump(json_encode([new Student(1, '测试一'), new Student(2, '测试二')], JSON_UNESCAPED_UNICODE)); // string(137) "[{"id":1,"name":"测试一","cat":["学生"],"number":"学号:1713936069","edu":{}},[2,"测试二",["学生"],"学号: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
本文转载自:https://juejin.cn/post/7001652041814638600
作者:硬核项目经理
추천 학습: "PHP 비디오 튜토리얼"
위 내용은 PHP의 JSON 관련 함수에 대해 이야기해 보겠습니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!