摘要开发中经常遇到看起来像列表/对象的字符串怎么转成真正的Python对象本文从三个场景展开标准JSON字符串转列表、类列表字符串的安全解析、以及RAG项目中Document对象的元数据提取。读完即用。一、场景一标准JSON字符串转列表这是最简单的情况。字符串本身就是合法的JSON数组。pythonimport json raw_str [qwen-turbo,qwen-plus,deepseek] result json.loads(raw_str) print(result) print(type(result))输出text[qwen-turbo, qwen-plus, deepseek] class listjson.loads()一步到位干净利落。也可以用ast.literal_evalpythonimport ast raw_str [qwen-turbo,qwen-plus,deepseek] result ast.literal_eval(raw_str)ast.literal_eval更安全只能解析合法的Python字面量不会执行恶意代码。不过JSON格式用json.loads更标准。二、场景二看起来像列表但实际是对象的字符串开发中常遇到这种数据——打印出来是[Document(...), Document(...)]看着像列表实际是一个字符串不能直接用json.loads解析。比如RAG检索返回的Document列表被转成字符串打印到日志里pythonraw_str [Document(metadata{pk: 12, page: 2}, page_content内容1), Document(metadata{pk: 27, page: 2}, page_content内容2)]目标提取每个Document的page属性。解法正则提取。pythonimport re raw_str 你的长字符串... # 提取所有page值 pages re.findall(rpage:\s*(\d), raw_str) pages [int(p) for p in pages] print(pages)输出text[2, 2, 3]如果想同时提取page和page_contentpythonimport re doc_pattern rDocument\(metadata\{([^}])\}, page_content([^]*) matches re.findall(doc_pattern, raw_str) for metadata, content in matches: page_match re.search(rpage:\s*(\d), metadata) if page_match: page int(page_match.group(1)) print(fpage: {page}, content预览: {content[:50]}...)关键点这种字符串的本质是对象的repr()输出不是标准序列化格式。正则是最直接的处理方式。三、场景三更复杂的Document字符串解析如果字符串中page_content包含换行符、引号等特殊字符情况会复杂一些。比如pythonraw_str [Document(metadata{page: 1}, page_content包含\引号\和\\n换行的内容)]简单正则可能匹配失败。这时可以分两步先按Document(分割再用括号匹配提取内容pythonimport re def extract_docs_from_str(raw_str): # 按Document(分割 parts raw_str.split(Document()[1:] results [] for part in parts: # 找到metadata部分 meta_match re.search(rmetadata\{([^}])\}, part) if not meta_match: continue metadata_str meta_match.group(1) page_match re.search(rpage:\s*(\d), metadata_str) page int(page_match.group(1)) if page_match else None # 找到page_content部分从第一个引号到最后一个引号 content_match re.search(rpage_content(.*)\)?\s*$, part, re.DOTALL) if not content_match: content_match re.search(rpage_content(.*)\)?\s*$, part, re.DOTALL) content content_match.group(1) if content_match else results.append({page: page, content: content}) return results注意对于嵌套引号和转义字符正则的健壮性有限。如果数据来源可控建议在数据源头用json.dumps()或pickle序列化不要用repr()打印后再解析。四、三种方案的对比场景数据格式推荐方案JSON数组[a,b]json.loads()Python字面量[1,2,3]或{a:1}ast.literal_eval()对象repr输出[Document(...)]正则提取五、踩坑提醒1. 永远不要用eval()python# ❌ 危险可能执行恶意代码 result eval(raw_str)eval()会执行字符串中的任意代码。如果数据来自外部输入攻击者可以注入__import__(os).system(rm -rf /)之类的代码。2. 区分字符串来源来自print()打印的对象 → 用正则来自json.dumps()→ 用json.loads()来自str(list)或repr()→ 用正则或ast.literal_eval()如果格式合法3. 单引号和双引号要统一json.loads()要求双引号。如果字符串是单引号包裹的JSON先用replace(, )转换但要注意内容里的引号不会误转。六、总结方法适用场景安全性json.loads()标准JSON安全ast.literal_eval()Python字面量安全正则提取对象repr输出安全eval()不推荐任何场景危险记住一句话能用json.loads就用json.loads不能用就上正则永远别用eval。