前阵子帮团队处理一批接口数据同事把几十万条记录导出成JSON文件结果接手的同学只会用Python另一个小组却要Java直接消费这批数据。折腾了半天我索性把主流的JSON解析方案都过了一遍才发现这个看似入门级的话题真展开聊能写不少东西。JSONJavaScript Object Notation现在几乎就是数据交换的默认格式配置文件、API返回、日志采集、AI工作流参数、甚至小说阅读器的书源配置到处都是它的影子。这篇文章我先聊聊自己在Python里的用法和踩过的坑再逐个拆解 JavaScript、Java、Go、C#、PHP、Rust 这些语言各自的解析方式帮你搞清楚不同项目里到底该选谁。1. 先搞清楚JSON解析到底在干什么1.1 把字符串变回数据结构这才是核心很多人一上来就背API什么json.loads、JSON.parse其实没搞明白底层在干嘛。JSON本质上就是一串有格式的文本语言要做的第一件事是把这个纯文本拆成内存里的结构体Python里的字典和列表、Java里的Map和List、Go里的struct和slice。这个拆的过程叫反序列化也就是解析。反过来把内存对象输出成字符串叫序列化也就是生成JSON。所以不管什么语言核心矛盾都一样如何把文本里的括号、引号、冒号翻译成当前语言能直接操作的数据结构。动态语言通常翻译成字典/对象静态强类型语言要么翻译成无类型的Map要么按你定义好的类/结构体去填充字段。理解这一层后面所有语言的API看起来都不过是同一个思路的不同皮囊。1.2 为什么不同语言解法差别这么大同样是解析{name:张三,age:18}Python一行dict(json.loads(...))Java你得引入Jackson或者GsonGo需要定义结构体Rust甚至要先derive几个宏。差别不在JSON本身而在语言设计哲学。动态语言追求开发效率解析结果天然就是灵活的字典/对象改字段名、增删字段都不需要动类型定义。静态语言追求类型安全希望编译期就能发现字段写错、类型不匹配的问题所以解析通常要绑定到具体的类或结构体代价是样板代码多一些。没有孰优孰劣只有场景合不合适。接下来我把Python的日常用法讲透再横向对比其他语言这样你脑子里会有一个很清晰的坐标系。2. Python的JSON解析我的日常用法和隐藏坑2.1 基础四件套必须刻进肌肉记忆Python的json模块就四个核心方法我用烂了import json # 字符串转对象 data json.loads({name: 张三, age: 18}) print(data[name]) # 张三 # 对象转字符串 json_str json.dumps(data, ensure_asciiFalse) print(json_str) # {name: 张三, age: 18} # 文件读取解析 with open(data.json, r, encodingutf-8) as f: data json.load(f) # 对象写入文件 with open(out.json, w, encodingutf-8) as f: json.dump(data, f, ensure_asciiFalse, indent2)loads和dumps处理字符串load和dump处理文件对象别搞混。我见过有人把json.load扔进json.loads里报AttributeError就是因为这两个名字长得太像其实一个接收文件、一个接收字符串。2.2 类型映射表记住这些不会翻车JSON类型Python类型注意事项objectdict键永远是字符串arraylist对应json数组下标从0开始stringstr默认会转义非ASCII字符number (int)int大整数能直接表示number (real)float精度可能丢失true/falseTrue/False注意首字母大写nullNone不是Null也不是nonetrue、false、null在Python里是True、False、None反序列化时自动转了。但如果你手动拼JSON字符串可别写成True否则对方解析会直接报错。这个细节在写爬虫拼接请求体时特别容易踩。2.3 实战案例labelme多边形JSON怎么转txt热搜里有个词叫labelme多边形json转txt这正好是JSON解析的经典场景。用labelme标注图像后每张图生成一个同名JSON里面有个shapes数组每个元素包含label和points字段。我需要把这些多边形坐标批量提取出来转成YOLO或普通文本格式。import json with open(annotation.json, r, encodingutf-8) as f: data json.load(f) for shape in data[shapes]: label shape[label] points shape[points] # points是二维数组例如 [[x1,y1],[x2,y2],...] coords .join([f{p[0]:.2f},{p[1]:.2f} for p in points]) print(f{label} {coords})这段代码的核心逻辑就是先json.load把整个标注文件读成字典然后一层层往下取。你用Python写一次换Java、Go无非就是把取字段的方式改一改思路完全一样。类似的还有书源JSON、订阅源JSON、ComfyUI工作流JSON处理模式都是三步走读取文本、解析成对象、按字段遍历。2.4 小技巧和隐藏坑每条都是实测换来的第一ensure_asciiFalse一定要记住。json.dumps默认会把中文转成\uXXXX看的你头大。加上这个参数输出才是正常人能读的中文。文件读写时encodingutf-8也别忘了Windows下经常因为默认编码不对导致乱码。第二大JSON文件不要一把梭。如果你处理的是几百MB甚至几个GB的日志、导出数据json.load()会把整个文件读进内存直接爆内存。这时候要么改用ijson做流式解析要么先判断数据结构能不能按行切分。比如日志文件每行一个JSON对象就老老实实按行读import json results [] with open(big.log, r, encodingutf-8) as f: for line in f: line line.strip() if line: data json.loads(line) results.append(data)第三json.dumps遇到不认识的类型会报错。比如字典里有个datetime对象直接dumps会告诉你Object of type datetime is not JSON serializable。解决办法是给default参数传一个自定义转换函数import json from datetime import datetime def convert(obj): if isinstance(obj, datetime): return obj.isoformat() raise TypeError(fType {type(obj)} not serializable) data {time: datetime.now()} print(json.dumps(data, defaultconvert))第四重复键不会报错。json.loads({a:1,a:2})会静默取最后一个值。如果上游数据不可信建议解析前做一次校验或者用object_pairs_hook去检测重复键别等线上出bug再排查。3. 除了Python这些语言解析JSON也很顺手3.1 JavaScript/TypeScript真正的嫡系JSON全称就是JavaScript Object Notation所以JS解析JSON几乎是零成本。浏览器和Node.js都内置了JSON对象直接JSON.parse()和JSON.stringify()不需要装任何依赖// 浏览器或Node.js环境 const obj JSON.parse({name:张三,age:18}); console.log(obj.name); // 张三 const str JSON.stringify(obj); console.log(str);在实际工程里常配合文件操作一起用。Node.js里读取JSON文件要注意编码最好显式指定utf8const fs require(fs); const data JSON.parse(fs.readFileSync(data.json, utf8));写回文件时想要格式化输出就用JSON.stringify(obj, null, 2)第二个参数是替换函数第三个是缩进空格数这个2是我最常用的。TypeScript则更进一步可以在解析后做类型断言interface Person { name: string; age: number; } const obj JSON.parse({name:张三,age:18}) as Person; console.log(obj.name.toUpperCase());不过要注意as Person只是编译期类型断言运行时JSON里如果缺了name字段照样是undefined不会像Java、Go那样强校验。TypeScript的类型安全更多是开发期的保障不是运行期的护身符。3.2 JavaJackson和Gson到底选谁Java没有内置JSON解析标准库早年最流行的是Google的Gson和 FasterXML的Jackson。现在企业级项目里Jackson基本是事实标准Spring Boot默认集成也是它。Jackson核心用法如下import com.fasterxml.jackson.databind.ObjectMapper; ObjectMapper mapper new ObjectMapper(); // JSON字符串转Map MapString, Object map mapper.readValue(jsonStr, new TypeReferenceMapString, Object() {}); // JSON字符串转Java对象推荐 Person person mapper.readValue(jsonStr, Person.class); // Java对象转JSON字符串 String jsonStr mapper.writeValueAsString(person);如果你的数据字段很多我更推荐直接定义POJOpublic class Person { private String name; private int age; // getter和setter必须写Jackson靠反射和getter/setter工作 }Java解析的典型痛点有几个一是ObjectMapper建议复用同一个实例因为它是线程安全的频繁new性能损耗很大二是日期字段要单独配置格式三是如果JSON里某个字段在Java类里不存在默认会抛UnrecognizedPropertyException这时候可以配置mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);Gson的用法更轻量Gson gson new Gson(); Person person gson.fromJson(jsonStr, Person.class); String json gson.toJson(person);Gson对没有getter/setter的类也能处理反射得更彻底但性能略逊于Jackson。我的建议是Spring全家桶项目直接用Jackson不需要引入额外依赖独立的小工具、Android项目可以考虑Gson。3.3 Go结构体标签把字段映射写脸上Go的encoding/json是标准库里最常用的包它的特点是通过struct tag来声明字段映射关系一眼就能看出来JSON字段叫什么、选填还是必填package main import ( encoding/json fmt ) type Person struct { Name string json:name Age int json:age,omitempty City string json:city,omitempty } func main() { jsonStr : {name:张三,age:18} var p Person err : json.Unmarshal([]byte(jsonStr), p) if err ! nil { fmt.Println(解析失败:, err) return } fmt.Printf(%v\n, p) // 结构体转JSON out, _ : json.Marshal(p) fmt.Println(string(out)) }Go解析JSON有几个必须注意的地方。第一结构体字段必须首字母大写否则包外不可见json.Unmarshal根本填不进去。这大概是新手最容易踩的坑。第二omitempty的意思是字段值为零值时序列化时省略掉这很适合处理可选字段。第三解析数字默认是float64如果你直接断言成整数类型会报错。这是Go被吐槽最多的一点。如果你不想定义结构体也可以用map[string]interface{}但使用体验很差类型断言写到你怀疑人生。所以在Go里能定义结构体就定义结构体不要偷懒。3.4 C#Newtonsoft.Json 与 System.Text.Json 的迭代C#阵营曾经是Newtonsoft.Json也叫Json.NET的天下功能和灵活性都是顶级的几乎无所不能。从.NET Core 3.0开始微软官方推出了System.Text.Json性能比Newtonsoft好不少现在的新项目我通常优先用它。using System.Text.Json; var jsonStr {\name\:\张三\,\age\:18}; var options new JsonSerializerOptions { PropertyNameCaseInsensitive true }; var person JsonSerializer.DeserializePerson(jsonStr, options); public class Person { public string Name { get; set; } public int Age { get; set; } }System.Text.Json默认是大小写敏感的所以配置PropertyNameCaseInsensitive true可以避免JSON里name和C#属性Name对不上的烦恼。如果还想要和JSON字段名完全不同的映射用[JsonPropertyName(full_name)]特性。Newtonsoft.Json的优势在于生态成熟很多老项目都在用网上资料也多。如果你的项目是.NET 6且不需要老库的特殊功能直接用System.Text.Json就够了如果你是维护Legacy项目别纠结继续用Newtonsoft就行。能用新库别守旧能少一个依赖是一个依赖。3.5 PHPjson_decode一行搞定但不代表没坑PHP的JSON支持非常无脑$jsonStr {name:张三,age:18}; $data json_decode($jsonStr, true); echo $data[name]; // 不传第二个参数得到的是对象 $obj json_decode($jsonStr); echo $obj-name;第二个参数传true会转成关联数组不传则得到stdClass对象一般处理数组比对象方便所以我习惯传true。PHP解析JSON最大的坑是静默失败。如果JSON格式有问题json_decode返回null不报任何异常。这时候你继续操作$data[name]又是一通报错。判断是否解析成功必须检查错误$data json_decode($jsonStr, true); if (json_last_error() ! JSON_ERROR_NONE) { echo JSON解析错误: . json_last_error_msg(); }PHP 7.3以后可以传JSON_THROW_ON_ERROR标志直接抛异常try { $data json_decode($jsonStr, true, 512, JSON_THROW_ON_ERROR); } catch (JsonException $e) { echo 解析失败: . $e-getMessage(); }我强烈建议代码里加上异常处理不然排查问题全靠猜。3.6 Rustserde_json的强类型玩法Rust在JSON解析领域几乎是靠serde和serde_json打天下。serde是一个序列化/反序列化框架支持JSON、YAML、TOML、BSON等多种格式serde_json是把serde和JSON绑定起来的库。use serde::{Deserialize, Serialize}; #[derive(Debug, Serialize, Deserialize)] struct Person { name: String, #[serde(default)] age: u8, } fn main() - Result(), Boxdyn std::error::Error { let json_str r#{name:张三,age:18}#; let p: Person serde_json::from_str(json_str)?; println!({:?}, p); let out serde_json::to_string(p)?; println!({}, out); Ok(()) }Rust的解析有两个明显优势一是字段缺失可以在编译/运行期直接报错#[serde(default)]可以给缺失字段提供默认值二是性能和内存安全都很出色。代价是写起来比较啰嗦每个结构体都要写derive宏。如果你在写CLI工具、网络服务或者对性能极度敏感的场景Rust这套组合拳值得投入成本。3.7 终端里的jq不算语言但很能打严格来说jq不是编程语言但它是一个专门处理JSON的命令行工具几乎每个搞数据的同学都应该装上。用它可以在写任何脚本前先快速检查JSON结构# 格式化并高亮显示 cat data.json | jq . # 提取某个字段 cat data.json | jq .name # 遍历数组并取多个字段 cat data.json | jq [.items[].name]我的习惯是处理一个陌生JSON之前先jq .看一眼结构再写代码比打开编辑器看半天有效得多。它只能当辅助工具替代不了编程语言里那些业务逻辑处理但配合任何语言都很好用。4. 跨语言解析JSON的常见坑与排查技巧4.1 高频报错速查表我在实际项目里整理了一张速查表遇到类似报错直接对号入座语言/场景典型报错常见原因解决办法Pythonjson.decoder.JSONDecodeError: Expecting value: line 1 column 2字符串不是合法JSON比如空字符串、BOM头、多余逗号先打印原始字符串检查空白字符和编码PythonTypeError: Object of type datetime is not JSON serializable序列化时遇到非JSON类型给dumps传default函数Java/JacksonMismatchedInputException: Cannot deserialize value of type int from StringJSON里数字被写成字符串如age:18在POJO的setter里做转换或改用String接收再处理Java/启动器java.io.IOException: Unable to read version json目标JSON文件为空、下载不完整或格式损坏检查文件是否完整重新获取确认路径正确Gojson: cannot unmarshal string into Go struct field Person.age of type int同上字符串与int类型不匹配改结构体字段类型或写自定义UnmarshalJSONGojson: cannot unmarshal object into Go value of type []Something顶层是对象而不是数组jq .确认JSON结构再定义类型Rustmissing field \name反序列化时必填字段缺失补字段或用#[serde(default)]给默认值Rust/部分框架failed to deserialize the json body into the target type: input: missing fie...请求体里缺字段或字段名拼写不一致校验请求体schema检查字段名大小写C#JsonException: The JSON value could not be converted to System.Int32类型不匹配比如浮点数塞进int属性改用long、double或使用JsonSerializerOptions.NumberHandlingPHPjson_decode返回null且无异常JSON格式错误用json_last_error_msg()看具体错误原因4.2 编码、BOM与日期格式这三个坑最隐蔽编码问题最容易让人崩溃。很多Windows下生成的JSON文件自带UTF-8 BOM头python json.load会直接报Unexpected UTF-8 BOMJava的Jackson也可能解析失败。解决方案很简单读取文件时跳过BOM或指定编码# Python读取带BOM的UTF-8文件 with open(data.json, r, encodingutf-8-sig) as f: data json.load(f)// Java使用InputStreamReader指定UTF-8 ObjectMapper mapper new ObjectMapper(); JsonNode root mapper.readTree(new InputStreamReader(inputStream, StandardCharsets.UTF_8));日期格式不统一则是大型项目中永远的痛。有人用created:2026-05-01 12:00:00有人用created:2026-05-01T12:00:00Z还有人用时间戳created:1777536000。解析前先约定好规范比啥技巧都重要。如果历史数据没法改就得在代码里写兼容逻辑逐个判断类型再转换。数字精度也值得一提。JSON里的整数在不同语言里会被解析成不同的类型Python和Go把整数存成intJavaScript的JSON.parse把所有数字都当float64处理导致超过Number.MAX_SAFE_INTEGER的大整数精度丢失。如果你处理的是长ID比如雪花算法生成的ID用JS解析就危险了要么转成字符串、要么用BigInt别等数据对不上才想起来。4.3 性能与选型心得我的实际体验最后聊聊选型。很多人问到底用哪个语言解析JSON最好我的答案永远是看场景数据分析、小脚本、爬虫、AI训练数据处理无脑Python开发效率无敌json模块够用缺性能再加orjson一个更快的第三方库。Web前端、Node.js服务JavaScript/TypeScript内置JSON对象集成度最高几乎没有额外学习成本。企业后端、微服务Java用JacksonC#用System.Text.Json两者都是生态完善、性能在线、维护成本可控的选择。高性能API、云原生基础设施、CLI工具Go静态编译、内存占用低、并发能力强struct tag的写法越用越顺手。系统级组件、对安全和性能要求苛刻的场景Rust serde编译期就把类型问题全堵死。快速做Web接口、临时脚本PHP的json_decode是真的快前提是你记得做错误处理。性能对比上单次解析的差距在小数据量下几乎无感知但在每秒几十万次请求的高并发服务里就很明显了。Node.js的V8引擎、Go、Rust通常响应最快Python和PHP需要额外优化比如用更快的第三方解析库、避免无谓的对象拷贝。再补一个我自己习惯的工作流拿到任何一批JSON数据先jq .看结构再写个最小的测试文件跑通解析最后才去封装正式逻辑。这套流程帮我避开了无数低级错误。JSON解析本身不复杂复杂的是数据不规整、字段对不上、编码乱套。你把这个基本功打扎实了不管切到什么语言遇到问题都不慌。