从长字符串中解析合法json结构的示例

当LLM输出包含json结构时,如何解析是一个比较难处理的问题,因为除json外还有常规文本。

虽然prompt约定按```json{xxx}```输出,LLM依然有可能忽略```json ```直接输出json的body。

这里参考网络资料,尝试示例集中健壮性比较好的从字符串提取json的解析方案。

1 直接解析

如果整个字符串就是 JSON,直接使用 json.loads() 解析,示例如下

复制代码
import json
text = '{"name": "Alice", "age": 30}'
data = json.loads(text)

2 字符串匹配

json body本质上是可解析字符串,所以也可以尝试字符串匹配方法,确定json body范围。

2.1 正则匹配

如果字符串中混杂 JSON 和其他文本,可以采用正则匹配的方法,示例代码如下

复制代码
import re, json
text = '前面一些文字 {"name": "Bob", "age": 25} 后面还有文字'
match = re.search(r'(\{.*\}|\[.*\])', text, re.DOTALL)
if match:
    json_str = match.group(1)
    data = json.loads(json_str)

适用于 JSON 不包含嵌套同类型括号,且没有转义干扰。

即无法正确处理嵌套结构,如对象内嵌对象或数组。

此时会匹配到第一个 { 到最后一个 },若中间有其他 {} 可能出错。

然而,实际测试显示,这种方案也能兼容部分此类情况。

嵌套和转义示例如下。

复制代码
import re, json
text = '前面一些文字 {"name": "Bo{{{b", "age": 25, "d{a{{d": {"dda": true}} 后面还有文字'
match = re.search(r'(\{.*\}|\[.*\])', text, re.DOTALL)
if match:
    json_str = match.group(1)
    data = json.loads(json_str)
    print(data)

输出示例如下

{'name': 'Bo{{{b', 'age': 25, 'd{a{{d': {'dda': True}}

2.2 括号匹配

手动扫描字符串,找到第一个 {[,然后使用计数器匹配对应的闭合括号。

档json比较规范时,括号匹配是一种有效方法。

然而当json的key或value包含{等字符时,该方法失效,示例代码如下。

复制代码
def extract_json(s):
    start = None
    stack = []
    for i, ch in enumerate(s):
        if ch in '{[':
            if not stack:
                start = i
            stack.append(ch)
        elif ch in '}]':
            if stack and ((ch == '}' and stack[-1] == '{') or (ch == ']' and stack[-1] == '[')):
                stack.pop()
                if not stack:
                    return s[start:i+1]
    return None

text = '一些文字 {"d{ata": [1,2,3], "ok}{{dsa": true} 结尾'

json_str = extract_json(text)
print(print(text))
if json_str:
    data = json.loads(json_str)
    print(data)

输出示例如下

一些文字 {"d{ata": [1,2,3], "ok}{{dsa": true} 结尾

None

所以不要仅凭计数 {} 来提取 JSON,除非完全确定字符串内不含花括号。

只要字符串可能包含结构字符,就必须使用真正的 JSON 解析器。

3 标准库方法

字符串匹配方法存在以上所述缺陷,这里进一步尝试标准库方法。

3.1 raw_decode方法

使用json标准库的的raw_decode方法,即用json.JSONDecoder.raw_decode解析。

raw_decode 可以从字符串开头解析 JSON,并返回解析后的对象和结束位置。

raw_decode要求 JSON 必须出现在字符串开头,否则需要先定位。

复制代码
import json

def extract_json_from_anywhere(s):
    decoder = json.JSONDecoder()
    # 依次从每个可能的位置尝试解析
    for i in range(len(s)):
        if s[i] in '{[':
            try:
                obj, end = decoder.raw_decode(s[i:])
                return obj
            except json.JSONDecodeError:
                continue
    return None

text = '前缀 {"valid": true, "dab": {"dd{d": 123, "sta}tus": true}} 后缀'
data = extract_json_from_anywhere(text)
print(data)

输出示例如下,可见此类方法能兼容特殊情况。

{'valid': True, 'dab': {'dd{d': 123, 'sta}tus': True}}

3.2 jsonfinder方法

jsonfinder是专门提取字符串中的 JSON的第三方库。

安装方法如下

pip install jsonfinder

示例代码如下

复制代码
from jsonfinder import jsonfinder

# 你的原始字符串
text = '前缀 {"name": "Alice", "age": 30}后缀'

# 使用 jsonfinder 迭代提取 JSON 对象
# 每次迭代会返回 (start_index, end_index, parsed_json_object)
for start, end, obj in jsonfinder(text):
    # obj 是已经用 json.loads() 解析好的 Python 对象(dict 或 list)
    if obj:
        print(text[start:end])
        print(f"找到了 JSON:{obj}")

输出如下,可见jsonfinder也能从字符串中提取正确的json数据。

{"name": "Alice", "age": 30}

找到了 JSON:{'name': 'Alice', 'age': 30}

reference


jsonfinder

https://github.com/alexmojaki/jsonfinder

在Python中读取大型JSON文件(raw_decode)

https://dev59.com/nYbca4cB1Zd3GeqPaMnZ

相关推荐
ZC跨境爬虫3 小时前
3D地球卫星轨道可视化平台开发Day2(轨道错位Bug修复+模块化结构优化)
前端·3d·html·json·bug
ZC跨境爬虫3 小时前
3D 地球卫星轨道可视化平台开发 Day1(3D 场景、卫星渲染与筛选交互实现)
前端·3d·html·json·交互
20YC编程社区1 天前
一分钟了解JSON格式,使用场景,和它的优缺点
json
二月十六1 天前
SQL Server 2022 新特性:JSON_OBJECT、JSON_ARRAY、JSON_PATH_EXISTS 详解
json·sqlserver 、
HIT_Weston1 天前
47、【Agent】【OpenCode】本地代理增强版分析(JSON解析)
人工智能·json·agent·opencode
SuperEugene2 天前
Vue3 配置驱动弹窗:JSON配置弹窗内容/按钮,避免重复开发弹窗|配置驱动开发实战篇
前端·javascript·vue.js·前端框架·json
五仁火烧2 天前
前端最常用的两种请求数据格式application/json 和 multipart/form-data 完全解析
前端·javascript·vue.js·json
吹个口哨写代码2 天前
h5/小程序直接读本地/在线的json文件数据
前端·小程序·json
DevOpenClub3 天前
文章抽取信息化 JSON API 接口
json