从长字符串中解析合法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:i1] 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---jsonfinderhttps://github.com/alexmojaki/jsonfinder在Python中读取大型JSON文件raw_decodehttps://dev59.com/nYbca4cB1Zd3GeqPaMnZ