你是否经常需要在一大坨 JSON 里找某个嵌套字段的值?用 grep 不精准,用 jq 语法又太难记。今天我们用 OpenCode 花 3 分钟写一个轻量级命令行 JSON 查询器——用直观的点号路径(如 users.0.name)就能快速提取或修改 JSON 中的任意嵌套值。
技术栈:Python 3,纯标准库(argparse + json),零依赖。
最终代码:约 90 行,完整可运行。
最终效果:
$ python jsonquery.py sample.json users.0.name
张三
$ python jsonquery.py sample.json config.limits -p
{
"max_upload": 50,
"max_connections": 100
}
$ python jsonquery.py sample.json -k
users
config
opencode CLI 模式)在终端输入 opencode,看到交互界面后就可以开始了。
我对 OpenCode 说:
> 写一个 Python 函数 json_get(obj, path),obj 是 dict 或 list,path 是点号分隔的字符串如 "users.0.name"。要求支持字典的字符串 key 和列表的数字索引,找不到时返回 None。
OpenCode 生成了:
def json_get(obj, path):
keys = path.split(".")
current = obj
for key in keys:
if isinstance(current, list):
try:
current = current[int(key)]
except (ValueError, IndexError):
return None
elif isinstance(current, dict):
current = current.get(key)
if current is None:
return None
else:
return None
return current
寥寥 14 行,逻辑清晰:按点号切分路径 → 逐层递进 → 列表用数字下标、字典用 key 查询。OpenCode 很聪明地处理了异常边界——比如路径不存在或类型不匹配时返回 None。
光能查还不够,有时候我们需要把某个字段改了再输出整个 JSON。
我对 OpenCode 说:
> 再加一个 json_set(obj, path, value) 函数,支持同样的点号路径,在指定位置设置新值,返回布尔值表示成功或失败。
OpenCode 生成了:
def json_set(obj, path, value):
keys = path.split(".")
current = obj
for key in keys[:-1]:
if isinstance(current, list):
try:
current = current[int(key)]
except (ValueError, IndexError):
return False
elif isinstance(current, dict):
if key not in current:
return False
current = current[key]
else:
return False
last = keys[-1]
if isinstance(current, list):
current[int(last)] = value
elif isinstance(current, dict):
current[last] = value
return True
OpenCode 的亮点:它知道遍历到倒数第二个 key 就停下,然后再根据最后一级的容器类型来决定是 current[int(last)] = value 还是 current[last] = value。这种细节如果是手写可能会漏掉。
核心逻辑有了,下一步是让它变成真正的命令行工具。
我对 OpenCode 说:
> 用 argparse 把这两个函数封装成命令行工具。支持参数:
> - 位置参数 file(可选,不传从 stdin 读)
> - 位置参数 path
> - -s / --set:设置值
> - -p / --pretty:格式化输出
> - -k / --keys:列出顶层 key
> 另外从 stdin 读时用 sys.stdin。
OpenCode 生成了一个完整的 main() 函数,包括从文件/stdin 读取 JSON、根据参数分发到不同模式,这里摘录关键部分:
parser = argparse.ArgumentParser(description="用点号路径查询和修改 JSON 数据")
parser.add_argument("file", nargs="?", help="JSON 文件路径(省略则从 stdin 读取)")
parser.add_argument("path", nargs="?", help='查询路径,如 "users.0.name"')
parser.add_argument("-s", "--set", help="设置指定路径的值")
parser.add_argument("-p", "--pretty", action="store_true", help="格式化输出")
parser.add_argument("-k", "--keys", action="store_true", help="列出顶层键")
设置模式尤其聪明——它会先用 json.loads() 解析用户传入的值字符串,然后调用 json_set,最后把整个修改后的 JSON 原样打印出来,这样就能通过管道传给下一个命令:
$ python jsonquery.py sample.json status --set '"offline"' | curl -X POST -d @- http://api.example.com/update
代码写完了,一般还得自己测。但我直接对 OpenCode 说:
我对 OpenCode 说:
> 写一个 sample.json 文件,包含嵌套的 users 数组和 config 对象,然后分别测试查询单个值、列出全部键、格式化输出嵌套对象这三个功能。
OpenCode 一秒写完测试数据和验证命令,运行结果一切正常——users.0.name 返回"张三",-k 列出 users 和 config,config.limits -p 格式化输出两个限制参数。
<details>
<summary>点击展开测试用 sample.json</summary>
{
"users": [
{"name": "张三", "email": "zhangsan@example.com", "roles": ["admin", "editor"]},
{"name": "李四", "email": "lisi@example.com", "roles": ["viewer"]}
],
"config": {
"version": "1.0",
"debug": true,
"limits": {"max_upload": 50, "max_connections": 100}
}
}
</details>
<details>
<summary>jsonquery.py(90 行,点击展开)</summary>
#!/usr/bin/env python3
"""JSON Path Query Tool - 用点号路径查询和修改 JSON 数据"""
import json
import sys
import argparse
def json_get(obj, path):
keys = path.split(".")
current = obj
for key in keys:
if isinstance(current, list):
try:
current = current[int(key)]
except (ValueError, IndexError):
return None
elif isinstance(current, dict):
current = current.get(key)
if current is None:
return None
else:
return None
return current
def json_set(obj, path, value):
keys = path.split(".")
current = obj
for key in keys[:-1]:
if isinstance(current, list):
try:
current = current[int(key)]
except (ValueError, IndexError):
return False
elif isinstance(current, dict):
if key not in current:
return False
current = current[key]
else:
return False
last = keys[-1]
if isinstance(current, list):
current[int(last)] = value
elif isinstance(current, dict):
current[last] = value
return True
def main():
parser = argparse.ArgumentParser(description="用点号路径查询和修改 JSON 数据")
parser.add_argument("file", nargs="?", help="JSON 文件路径(省略则从 stdin 读取)")
parser.add_argument("path", nargs="?", help='查询路径,如 "users.0.name"')
parser.add_argument("-s", "--set", help="设置指定路径的值")
parser.add_argument("-p", "--pretty", action="store_true", help="格式化输出")
parser.add_argument("-k", "--keys", action="store_true", help="列出顶层键")
args = parser.parse_args()
if args.file:
with open(args.file, "r", encoding="utf-8") as f:
data = json.load(f)
else:
data = json.load(sys.stdin)
if args.keys:
if isinstance(data, dict):
for k in data:
print(k)
elif isinstance(data, list):
for i in range(len(data)):
print(i)
return
if args.set is not None:
value = json.loads(args.set)
if json_set(data, args.path, value):
print(json.dumps(data, indent=2, ensure_ascii=False))
else:
print(f"错误: 无法设置路径 '{args.path}'", file=sys.stderr)
sys.exit(1)
return
result = json_get(data, args.path)
if result is None:
print("null")
elif args.pretty:
print(json.dumps(result, indent=2, ensure_ascii=False))
else:
if isinstance(result, (dict, list)):
print(json.dumps(result, ensure_ascii=False))
else:
print(result)
if __name__ == "__main__":
main()
</details>
这个 JSON 查询器从头到尾写了 4 轮对话,每次就一句自然语言描述需求。对比一下效率:
| 环节 | OpenCode 方式 | 传统手写方式 |
|------|-------------|------------|
| 调研 API | 0 分钟(自动选用 json + argparse) | 5 分钟(查文档) |
| 核心代码 | 14 行,30 秒生成 | 14 行,约 5 分钟(含调试边界) |
| argparse 封装 | 40 行,1 分钟生成 | 40 行,约 10 分钟 |
| 测试验证 | 1 句话生成测试数据 + 命令 | 手动构造数据,约 5 分钟 |
| 总耗时 | ~3 分钟 | ~25 分钟 |
OpenCode 最让我舒服的一点是:不用记 API。无论 json.loads 的参数还是 argparse.ArgumentParser 的用法,你只管描述需求,它负责查文档、写代码、处理边界。你真正花时间的是思考"我要做什么",而不是"这个函数第三个参数该传什么"。
下篇预告:用 OpenCode 开发现代 CLI 工具的必修课——带颜色输出、进度条和 spinner 的终极大改造。