JSON和URL编码解决的是两个不同层面的问题:JSON负责结构化描述数据,URL编码负责让数据安全地出现在URL中。实际开发中,经常需要把JSON对象作为查询参数、接口参数或跳转地址的一部分传递,这时不能简单地将JSON字符串直接拼接到URL中,而应该先完成JSON序列化,再进行URL编码。
JSON数据为什么需要进行URL编码
JSON通常包含大括号、双引号、冒号、逗号等特殊字符,例如:
JSON{"name":"张三","age":18}
如果直接拼接到URL中:
https://example.com/api?data={"name":"张三","age":18}
虽然部分浏览器或服务端可能能够正常处理,但这种方式存在明显风险。
URL本身具有特定的语法结构,例如?用于开始查询参数,&用于分隔参数,=用于连接参数名和参数值。如果JSON内容中恰好包含这些特殊字符,就可能导致服务端错误解析。
例如:
JSON{"name":"Tom&Jerry","age":18}
直接放进URL:
https://example.com/api?data={"name":"Tom&Jerry","age":18}
其中的&可能被解析为新的查询参数分隔符,从而破坏原有数据结构。
URL编码的作用,就是将这些可能产生歧义的字符转换成安全的百分号编码形式。
例如:
{"name":"Tom&Jerry","age":18}
编码后类似于:
%7B%22name%22%3A%22Tom%26Jerry%22%2C%22age%22%3A18%7D
这样数据就可以作为URL参数安全传输。
JSON转换为URL编码的基本原理
完整过程实际上可以拆分为两个步骤:
JSON对象 ↓ JSON序列化 ↓ JSON字符串 ↓ URL编码 ↓ URL安全字符串
例如JavaScript中:
JavaScriptconst data = { name: "张三", age: 18 }; const json = JSON.stringify(data); const encoded = encodeURIComponent(json); console.log(encoded);
JSON.stringify()负责把JavaScript对象转换成JSON字符串,而encodeURIComponent()负责对字符串进行URL编码。
最终可以得到类似:
%7B%22name%22%3A%22%E5%BC%A0%E4%B8%89%22%2C%22age%22%3A18%7D
如果需要将它放入完整URL,可以这样处理:
JavaScriptconst url = "/api/user?data=" + encodeURIComponent(JSON.stringify(data)); console.log(url);
这种写法更加安全,也更符合URL参数的处理方式。
URL编码到底转换了什么
URL编码通常采用百分号编码,也就是将特定字符转换成%加两个十六进制字符。
例如:
{ → %7B } → %7D " → %22 : → %3A , → %2C & → %26
中文等非ASCII字符则通常按照UTF-8编码后,再转换成百分号形式。
例如:
张
UTF-8对应的字节经过URL编码后会表现为:
%E5%BC%A0
因此,JSON中的中文内容经过URL编码后,长度通常会明显增加。
需要注意的是,URL编码并不是加密。即使JSON被转换成了:
%7B%22username%22%3A%22admin%22%7D
任何人都可以通过URL解码还原出原始内容。因此,不能把URL编码当作数据加密或安全保护手段。
JavaScript实现JSON转URL编码
JavaScript是Web开发中处理JSON和URL编码最常见的语言之一。
使用encodeURIComponent
推荐的方式是:
JavaScriptconst user = { id: 1001, username: "张三", role: "admin" }; const jsonString = JSON.stringify(user); const encodedData = encodeURIComponent(jsonString); console.log(encodedData);
如果需要反向恢复:
JavaScriptconst jsonString = decodeURIComponent(encodedData); const user = JSON.parse(jsonString); console.log(user);
整个转换过程具有对称关系:
JSON.stringify() ↓ encodeURIComponent() ↓ URL传输 ↓ decodeURIComponent() ↓ JSON.parse()
这种方式特别适合前端页面跳转、查询参数以及简单的GET接口调用。
encodeURI和encodeURIComponent有什么区别
实际开发中经常有人把encodeURI()和encodeURIComponent()混在一起使用,两者用途并不相同。
encodeURI()主要用于编码完整URI,但会保留URL结构所需要的一些字符,例如:
: / ? & =
而encodeURIComponent()用于编码URL中的某一个组件,也就是参数值,因此会编码更多特殊字符。
假设JSON作为data参数:
JavaScriptconst json = JSON.stringify({ name: "Tom&Jerry", age: 18 }); const url = "/api?data=" + encodeURIComponent(json);
这里应该优先使用encodeURIComponent()。
原因很简单:JSON是参数值,而不是完整URL。
可以记住一个实用规则:
编码完整URL → encodeURI() 编码URL参数值 → encodeURIComponent()
Java实现JSON转URL编码
Java项目中通常会先使用JSON库完成序列化,再通过URL编码工具处理字符串。
以常见的Jackson为例:
JavaObjectMapper mapper = new ObjectMapper(); Map<String, Object> data = new HashMap<>(); data.put("name", "张三"); data.put("age", 18); String json = mapper.writeValueAsString(data); String encoded = URLEncoder.encode(json, StandardCharsets.UTF_8); System.out.println(encoded);
如果项目使用Gson,也可以采用类似思路:
JavaGson gson = new Gson(); Map<String, Object> data = new HashMap<>(); data.put("name", "张三"); data.put("age", 18); String json = gson.toJson(data); String encoded = URLEncoder.encode(json, StandardCharsets.UTF_8);
需要特别注意,Java中的URLEncoder主要针对application/x-www-form-urlencoded场景。它与JavaScript的encodeURIComponent()并不是完全相同的实现语义。
例如空格在表单编码中通常会转换成:
+
而标准百分号编码中常见的形式是:
%20
因此,如果前后端需要严格匹配编码规则,就应该提前确定接口所采用的编码规范。
Python实现JSON转URL编码
Python可以直接使用标准库完成JSON序列化和URL编码。
Python运行import json from urllib.parse import quote data = { "name": "张三", "age": 18 } json_string = json.dumps(data, ensure_ascii=False) encoded = quote(json_string) print(encoded)
如果需要还原:
Python运行from urllib.parse import unquote import json json_string = unquote(encoded) data = json.loads(json_string) print(data)
这里的处理顺序同样是:
dict ↓ json.dumps() ↓ JSON字符串 ↓ quote() ↓ URL编码
PHP实现JSON转URL编码
PHP中可以使用json_encode()生成JSON,再通过urlencode()或者rawurlencode()进行编码。
PHP$data = [ "name" => "张三", "age" => 18 ]; $json = json_encode($data, JSON_UNESCAPED_UNICODE); $encoded = rawurlencode($json); echo $encoded;
通常情况下,如果目标是RFC 3986风格的百分号编码,可以优先考虑rawurlencode()。
反向转换:
PHP$json = rawurldecode($encoded); $data = json_decode($json, true);
URL参数中传递JSON的完整示例
假设有一个搜索接口,需要将多个筛选条件封装成JSON:
JSON{ "keyword": "数据库", "page": 1, "pageSize": 20, "sort": "desc" }
前端可以这样构造URL:
JavaScriptconst filter = { keyword: "数据库", page: 1, pageSize: 20, sort: "desc" }; const params = encodeURIComponent(JSON.stringify(filter)); const url = `/search?filter=${params}`; console.log(url);
服务端收到filter后,首先进行URL解码,然后解析JSON:
URL参数 ↓ URL Decode ↓ JSON字符串 ↓ JSON Parse ↓ 业务对象
这也是JSON作为URL参数最典型的应用模式。
不要重复编码
JSON URL编码中另一个非常常见的问题是重复编码。
例如第一次:
JavaScriptconst encoded = encodeURIComponent(json);
已经完成编码。
如果后续又执行:
JavaScriptconst encodedAgain = encodeURIComponent(encoded);
就会产生二次编码。
例如原本的:
%7B
再次编码后可能变成:
%257B
因为%本身又被编码成了%25。
最终服务端如果只执行一次解码,得到的仍然不是原始JSON。
因此,应尽量保证:
JSON → 编码一次 → 传输 → 解码一次 → JSON
而不是:
JSON → 编码 → 再编码 → 传输
JSON URL编码后的数据如何解码
编码和解码必须保持对应关系。
JavaScript:
JavaScriptconst encoded = encodeURIComponent(JSON.stringify(data)); const json = decodeURIComponent(encoded); const result = JSON.parse(json);
Python:
Python运行decoded = unquote(encoded) data = json.loads(decoded)
Java:
JavaString json = URLDecoder.decode(encoded, StandardCharsets.UTF_8);
解码后得到JSON字符串,还需要继续执行JSON解析,才能恢复为对象。
因此:
URL解码 ≠ JSON解析
这是理解整个过程非常重要的一点。
URL解码只能把:
%7B%22name%22%3A%22%E5%BC%A0%E4%B8%89%22%7D
恢复成:
JSON{"name":"张三"}
而JSON.parse()、Jackson或Gson等工具才负责将JSON字符串转换成程序中的对象。
JSON放在URL中需要注意长度问题
虽然将JSON编码后放入URL非常方便,但并不意味着所有JSON数据都适合放到URL中。
例如:
JSON{ "keyword": "产品搜索", "filters": [ "手机", "电脑", "平板" ], "attributes": { "brand": "某品牌", "priceMin": 1000, "priceMax": 5000 } }
数据结构越复杂,编码后的URL越长。
过长的URL可能受到浏览器、Web服务器、代理服务器或网关的长度限制。因此,对于大量数据,不建议继续通过GET URL传输。
更合适的方式通常是使用POST请求,将JSON放在请求体中:
httpPOST /api/search Content-Type: application/json
请求体:
JSON{ "keyword": "数据库", "page": 1, "pageSize": 20 }
这样可以避免URL长度过长,同时也更适合复杂业务数据。
JSON编码和Base64不要混为一谈
开发中还有一种常见方案是:
JSON → Base64 → URL编码
例如:
JavaScriptconst json = JSON.stringify(data); const base64 = btoa(json); const encoded = encodeURIComponent(base64);
Base64和URL编码解决的问题不同。
JSON序列化解决的是对象到字符串的问题;Base64解决的是二进制或文本安全表示问题;URL编码解决的是URL特殊字符冲突问题。
它们之间没有谁一定能够替代谁。
如果JSON本身已经经过encodeURIComponent()处理,通常没有必要为了“安全”再额外做Base64。是否使用Base64,应根据接口协议、数据格式和兼容性要求决定。
JSON转换为URL编码的常见错误
直接拼接JSON
错误示例:
JavaScriptconst url = "/api?data=" + JSON.stringify(data);
JSON中的特殊字符可能影响URL解析。
更推荐:
JavaScriptconst url = "/api?data=" + encodeURIComponent(JSON.stringify(data));
先URL编码再JSON序列化
不要这样:
JavaScriptJSON.stringify(encodeURIComponent(data));
因为URL编码应该作用于最终需要传输的JSON字符串,而不是直接作用于JavaScript对象。
正确顺序是:
JavaScriptencodeURIComponent(JSON.stringify(data));
编码后再次编码
避免:
JavaScriptencodeURIComponent(encodeURIComponent(json));
除非接口协议明确要求进行多次编码,否则这很容易造成服务端解码异常。
忽略字符集
中文、日文、韩文以及其他非ASCII字符必须正确按照UTF-8进行处理。
前后端如果采用不同字符集,可能出现乱码或JSON解析失败。
如何选择合适的传输方式
可以根据数据特点进行判断:
| 场景 | 推荐方式 |
|---|---|
| 少量简单参数 | 普通URL查询参数 |
| 少量结构化参数 | JSON序列化后URL编码 |
| 页面跳转携带少量状态 | URL参数 |
| 复杂筛选条件 | POST + JSON |
| 大量结构化数据 | POST请求体 |
| 需要隐藏敏感信息 | 不应依赖URL编码,应考虑HTTPS和合理的安全方案 |
| 需要短链接 | 采用服务端ID映射,而不是直接塞入完整JSON |
尤其需要注意,URL中的参数通常会出现在浏览器历史记录、服务器访问日志、代理日志等位置。因此,密码、访问令牌、身份证号码等敏感信息不应该仅仅通过URL编码后放入查询参数。
实际开发中的推荐写法
如果使用JavaScript,最直接、清晰的方式是:
JavaScriptconst data = { userId: 1001, keyword: "数据库", page: 1 }; const query = encodeURIComponent(JSON.stringify(data)); const url = `/api/search?data=${query}`;
如果项目使用URLSearchParams,还可以进一步提高可读性:
JavaScriptconst data = { userId: 1001, keyword: "数据库", page: 1 }; const params = new URLSearchParams({ data: JSON.stringify(data) }); const url = `/api/search?${params.toString()}`;
这种方式能够让参数构建更加规范,也减少手动处理?、&和=等URL结构字符的麻烦。
总结
JSON数据转换为URL编码,本质上是两个连续的数据处理过程:先通过JSON序列化将对象转换成字符串,再对字符串进行URL编码,使其中的特殊字符能够安全地作为URL参数传输。
最核心的处理链路可以概括为:
对象 ↓ JSON序列化 ↓ JSON字符串 ↓ URL编码 ↓ URL参数
恢复时则反过来:
URL参数 ↓ URL解码 ↓ JSON字符串 ↓ JSON解析 ↓ 对象
开发过程中应重点关注编码方式、UTF-8字符集、重复编码、URL长度以及敏感数据泄露等问题。对于少量结构化参数,JSON配合URL编码非常实用;面对复杂或大体积数据,则更适合使用POST请求体传递JSON。理解这两种数据格式各自承担的职责,才能避免把JSON序列化、URL编码、Base64编码和数据加密混为一谈。