PHP JSON

json_encode/decode, Chinese characters and escaping, error handling, and array/object conversion.

Beginner
Key Points`json_decode($s, true)` returns arrays; without `true` it returns `stdClass` objects.

Encoding

PHP
<?php
$data = ["name" => "张三", "age" => 25, "tags" => ["php", "web"]];

echo json_encode($data), "\n";
echo json_encode($data, JSON_UNESCAPED_UNICODE), "\n";
echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT), "\n";
Output
{"name":"\u5f20\u4e09","age":25,"tags":["php","web"]}
{"name":"张三","age":25,"tags":["php","web"]}
{
    "name": "张三",
    "age": 25,
    "tags": [
        "php",
        "web"
    ]
}

By default non-ASCII becomes \uXXXX. **Use JSON_UNESCAPED_UNICODE for APIs** — smaller and easier to debug; keep the default for files and signatures.

Decoding

PHP
<?php
$json = '{"name":"张三","age":25,"tags":["php","web"]}';

$arr = json_decode($json, true);
echo $arr["name"], "\n";
echo count($arr["tags"]), "\n";
echo gettype($arr), "\n";

$obj = json_decode($json);
echo $obj->name, "\n";
echo $obj->tags[0], "\n";
echo gettype($obj), "\n";
Output
张三
2
array
张三
php
object

Without the second argument you get stdClass and use ->. Deep structures get verbose, so most projects pass true.

Error handling

json_decode returns null on failure. **Do not test === null alone** — decoding "null" also gives null. Use json_last_error().

PHP
<?php
foreach (['{"a":1}', '{bad}', 'null', ''] as $s) {
    $v = json_decode($s, true);
    $label = $s === "" ? "(空串)" : $s;

    if (json_last_error() === JSON_ERROR_NONE) {
        echo "$label => 成功: " . var_export($v, true) . "\n";
    } else {
        echo "$label => 失败: " . json_last_error_msg() . "\n";
    }
}
Output
{"a":1} => 成功: array (
  'a' => 1,
)
{bad} => 失败: Syntax error
null => 成功: NULL
(空串) => 失败: Syntax error

PHP 8.3 adds json_validate() — checks validity without building structures, saving memory on large payloads.

PHP
<?php
var_dump(json_validate('{"a":1}'));
var_dump(json_validate('{bad}'));

$v = json_decode('{"a":1}', false, 512, JSON_THROW_ON_ERROR);
echo $v->a, "\n";

try {
    json_decode('{bad}', false, 512, JSON_THROW_ON_ERROR);
} catch (JsonException $e) {
    echo "异常: " . $e->getMessage(), "\n";
}
Output
bool(true)
bool(false)
1
异常: Syntax error
JSON_THROW_ON_ERROR throws JsonException instead of relying on a later check — forgetting that check is the main source of these bugs.

Serving a JSON API

PHP
<?php
header("Content-Type: application/json; charset=utf-8");

$data = [
    "code" => 0,
    "msg"  => "ok",
    "data" => ["id" => 1, "name" => "张三"],
];

echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
Output
{"code":0,"msg":"ok","data":{"id":1,"name":"张三"}}

Content-Type must be application/json or fetch().json() may refuse. JSON_UNESCAPED_SLASHES keeps / unescaped in URLs.

Common pitfalls

SymptomCauseFix
json_encode 返回 false含非法 UTF-8 字节先 mb_convert_encoding 清洗
数字变成字符串原值就是字符串(如 "25")入库前转 int
小数精度丢失PHP float 是双精度金额用整数分存储
空数组变 [],空对象变 {}[] 编码为数组要对象就 (object)[]
中文乱码没设 UTF-8 头header("Content-Type: application/json; charset=utf-8")
PHP
<?php
echo json_encode([]), "\n";
echo json_encode((object)[]), "\n";
echo json_encode(["a" => []]), "\n";
echo json_encode(["a" => (object)[]]), "\n";

// 金额用整数分
$cents = 1999;
echo json_encode(["price" => $cents, "yuan" => $cents / 100]), "\n";
Output
[]
{}
{"a":[]}
{"a":{}}
{"price":1999,"yuan":19.99}
Floats accumulate error like 0.1 + 0.2 = 0.30000000000000004. Store integer cents and divide on display — standard in payments.

Friends & Partners