You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python内置json模块加载JSON时无法解析$ref指针的解决方法

问题说明

我创建了一份JSON配置文件,在顶层ColoringLight节点集中定义了语法高亮的全套配色值,syntax子节点下的各语法项通过{"$ref": "#/路径"}格式的JSON指针引用ColoringLight节点下的对应色值。使用Python内置的json模块加载该文件时,模块不会自动解析指针、替换为对应实际色值,会将$ref结构当作普通字典对象保留。需要实现通用加载逻辑:JSON存在指针时自动完成解析替换,JSON不含指针时也可正常加载。

备注:JSON标准本身并未内置指针解析的原生能力。

问题复现代码

测试代码:

import json
data = json.loads(open("test.json").read())
print(data)

# 原生加载输出结果:
# {'ColoringLight': {'bg': '#ffffff', 'fg': '#4f4c4d', 'TokenText': '#544619', 'TokenKeyword': '#2f33ff', 'TokenPunctuation': '#dd8ced', 'TokenName': '#b6c23f', 'TokenOperator': '#f6a656', 'TokenComment': '#2cbe48', 'TokenLiteralNumber': '#ab4bbe', 'TokenLiteralString': '#f45159'}, 'syntax': {'Token.Text': {'$ref': '#/ColoringLight/TokenText'}, 'Token.Keyword': {'$ref': '#/ColoringLight/TokenKeyword'}, 'Token.Punctuation': {'$ref': '#/ColoringLight/TokenPunctuation'}, 'Token.Name.Attribute': {'$ref': '#/ColoringLight/TokenName'}, 'Token.Operator': {'$ref': '#/ColoringLight/TokenOperator'}, 'Token.Comment': {'$ref': '#/ColoringLight/TokenComment'}, 'Token.Literal.Number.Bin': {'$ref': '#/ColoringLight/TokenLiteralNumber'}, 'Token.Literal.String.Affix': {'$ref': '#/ColoringLight/TokenLiteralString'}}}

对应test.json文件内容:

{
    "ColoringLight":
    {
        "bg": "#ffffff",
        "fg": "#4f4c4d",
        "TokenText": "#544619",
        "TokenKeyword": "#2f33ff",
        "TokenPunctuation": "#dd8ced",
        "TokenName": "#b6c23f",
        "TokenOperator": "#f6a656",
        "TokenComment": "#2cbe48",
        "TokenLiteralNumber": "#ab4bbe",
        "TokenLiteralString": "#f45159"
    },
    "syntax":
    {
        "Token.Text": {"$ref": "#/ColoringLight/TokenText"},
        "Token.Keyword": {"$ref": "#/ColoringLight/TokenKeyword"},
        "Token.Punctuation": {"$ref": "#/ColoringLight/TokenPunctuation"},
        "Token.Name.Attribute": {"$ref": "#/ColoringLight/TokenName"},
        "Token.Operator": {"$ref": "#/ColoringLight/TokenOperator"},
        "Token.Comment": {"$ref": "#/ColoringLight/TokenComment"},
        "Token.Literal.Number.Bin": {"$ref": "#/ColoringLight/TokenLiteralNumber"},
        "Token.Literal.String.Affix": {"$ref": "#/ColoringLight/TokenLiteralString"}
    }
}
实现方案

实现逻辑:

  • 先用原生json模块加载原始数据
  • 递归遍历所有字典、列表节点,识别仅包含$ref键的引用节点
  • 解析#/开头的本地指针路径,从根节点逐层查找对应实际值
  • 将引用节点替换为实际值,非引用节点原样返回,兼容无$ref的普通JSON场景

完整实现代码:

import json

def resolve_json_refs(obj, root=None):
    # 首次调用时将根节点绑定为原始加载对象
    if root is None:
        root = obj
    # 递归处理字典节点
    if isinstance(obj, dict):
        # 匹配$ref引用结构
        if len(obj) == 1 and "$ref" in obj:
            ref_path = obj["$ref"]
            # 处理本地JSON指针
            if ref_path.startswith("#/"):
                path_parts = ref_path[2:].split("/")
                current = root
                for part in path_parts:
                    current = current[part]
                return current
        # 普通字典递归处理所有值
        return {key: resolve_json_refs(value, root) for key, value in obj.items()}
    # 递归处理列表节点
    elif isinstance(obj, list):
        return [resolve_json_refs(item, root) for item in obj]
    # 基础类型(字符串、数字、布尔、None)直接返回
    else:
        return obj

# 调用示例
if __name__ == "__main__":
    with open("test.json", "r", encoding="utf-8") as f:
        raw_data = json.load(f)
    final_data = resolve_json_refs(raw_data)
    print(final_data)

效果说明

运行后syntax节点下所有引用都会被替换为ColoringLight中定义的实际色值,例如Token.Text对应值直接为#544619,不再保留$ref字典结构。如果加载的JSON中不存在任何$ref字段,函数返回结果和原生json.load完全一致,无额外副作用。


内容的提问来源于stack exchange,提问作者TRCK

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 01:30:35