radio 的 value 必须是字符串,对象需 JSON.stringify() 序列化后赋值;更安全做法是 value 存 ID、data-* 存对象,或改用 hidden + JS 同步数据。

radio 的 value 只能是字符串,对象得自己序列化
HTML 原生 的 value 属性只接受字符串,直接写 value={obj} 或 value="{{ item }}" 会转成 [object Object],后端收不到结构化数据。必须手动把对象转成 JSON 字符串塞进 value,提交后再解析。
- ✅ 正确做法:
value='{"id":1,"name":"张三","role":"admin"}'(注意单引号包裹整个 JSON,内部双引号) - ❌ 错误写法:
value={user}(React/Vue 模板中未字符串化)、value="{{ user }}"(Django/Jinja 中未|json过滤) - ⚠️ 特别注意空格和引号嵌套:用
JSON.stringify(obj)生成,别手敲;服务端收到后要用json.loads()或JSON.parse()解析
用 data-* 属性存原始对象,value 只作唯一标识
更安全的做法是让 value 保持简洁(比如 ID),把完整对象存在 data-* 自定义属性里,JS 提交前再取出来组合。这样避免 JSON 字符串被 HTML 编码破坏,也方便调试。
- 示例:
- 提交时 JS 获取:
document.querySelector('input[name="user"]:checked')?.dataset.user,然后JSON.parse() - 适用场景:表单不走原生 submit,而是用
fetch或axios手动构造 payload - ⚠️ 注意:
dataset读取时会自动小驼峰化(data-user-info→userInfo),但纯data-user不变
后端接收时别直接信任 radio value 的 JSON 格式
用户可以轻易改写 HTML 或发伪造请求,value 里的 JSON 字符串可能格式错误、字段缺失,甚至含恶意内容。后端必须做严格校验,不能无脑 json.loads(request.form['user'])。
- Python Flask 示例:先 try/except 捕获
JSONDecodeError,再用 Pydantic 或dict.get()安全取字段 - Node.js Express 示例:用
zod或joi验证解析后的对象结构,拒绝id非数字、name超长等非法输入 - 关键点:radio 的 value 是用户可控输入,和 query string、body 一样需同等防护级别
复杂场景建议放弃 radio + 对象,改用 hidden + JS 控制
当单选选项对应的数据结构差异大(比如有的带图片 URL,有的含嵌套权限数组),硬塞进 value 或 dataset 会让模板臃肿、难维护。不如用 radio 只控制「选中状态」,真实数据存在独立 中,由 JS 同步更新。
- 结构示例:
Pro版+ - JS 监听:
document.querySelectorAll('input[name="plan"]').forEach(r => r.addEventListener('change', () => { document.getElementById('selected-plan-data').value = JSON.stringify(planMap[r.value]); })) - 优势:HTML 干净,数据逻辑集中,支持动态加载选项而无需重写所有 radio 标签
实际项目里,多数人卡在第一步——忘了 value 必须是字符串。JSON 序列化那行代码漏掉,后面全白搭。另外,data-user 看似方便,但如果页面有 CSP 限制 inline script,dataset 读取也可能被拦截,得提前测。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











