从长字符串中解析合法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

相关推荐
老神在在0014 小时前
jsonshema小点
python·json
阿Y加油吧1 天前
RAG 必学:ANN 检索、HNSW 算法与 Milvus 核心概念详解
数据库·mysql·json
likerhood2 天前
Fastjson中的JSON.parseObject()详细讲解
java·json
老神在在0012 天前
JSON Schema
测试工具·json
xiaodaoluanzha2 天前
golang中MetaMessage(mm)的使用
json·protobuf
星空椰3 天前
从零到实战:一套完整的 Python 爬虫技术体系(requests + BeautifulSoup + 正则 + JSON)
爬虫·python·json·beautifulsoup
A__tao4 天前
JSON 转 Proto 工具(支持嵌套与注释解析)
json
A__tao4 天前
JSON 转 Java 实体类工具(支持嵌套与注释解析)
java·python·json
迷路爸爸1804 天前
VSCode / Cursor 中 LaTeX Workshop 的 settings.json 配置:编译与 SyncTeX 跳转
ide·vscode·json·latex
吴声子夜歌5 天前
Node.js——JSON-Server轻量级RESTful API
node.js·json·restful·json-server