如何自动修复无效的 JSON 字符串?
Posted
技术标签:
【中文标题】如何自动修复无效的 JSON 字符串?【英文标题】:How do I automatically fix an invalid JSON string? 【发布时间】:2013-09-02 02:54:09 【问题描述】:从 2gis API 我得到以下 JSON 字符串。
"api_version": "1.3",
"response_code": "200",
"id": "3237490513229753",
"lon": "38.969916127827",
"lat": "45.069889625267",
"page_url": null,
"name": "ATB",
"firm_group":
"id": "3237499103085728",
"count": "1"
,
"city_name": "Krasnodar",
"city_id": "3237585002430511",
"address": "Turgeneva, 172/1",
"create_time": "2008-07-22 10:02:04 07",
"modification_time": "2013-08-09 20:04:36 07",
"see_also": [
"id": "3237491513434577",
"lon": 38.973110606808,
"lat": 45.029031222211,
"name": "Advance",
"hash": "5698hn745A8IJ1H86177uvgn94521J3464he26763737242Cf6e654G62J0I7878e",
"ads":
"sponsored_article":
"title": "Center "ADVANCE"",
"text": "Business.English."
,
"warning": null
]
但 Python 无法识别:
json.loads(firm_str)
期望,分隔符:第 1 行第 3646 列(字符 3645)
这看起来像是一个问题的引号: "title": "中心 "ADVANCE""
如何在 Python 中自动修复它?
【问题讨论】:
这是编码问题,不是 JSON 问题。 编码正确。不要注意奇怪的字符 你能把它隔离成一个具体的小例子吗?移除碎片,直到剩下断裂的部分。 我认为问题在于用双引号分隔的字符串中有双引号。请改用"title": "bla 'ADVANCE'"
或"title": 'bla "ADVANCE"'
。应该可以构建一个正则表达式来找到那些......
考虑如果更高版本的 API 修复了这个错误会发生什么。确保当他们修复他们的代码时,您使用的任何解决方法都不会导致您的代码出现新错误。
【参考方案1】:
answer by @Michael 给了我一个想法......不是一个非常漂亮的想法,但它似乎工作,至少在你的例子中:尝试解析 JSON 字符串,如果它失败,查找它所在的字符在异常字符串1 中失败并替换该字符。
while True:
try:
result = json.loads(s) # try to parse...
break # parsing worked -> exit loop
except Exception as e:
# "Expecting , delimiter: line 34 column 54 (char 1158)"
# position of unexpected character after '"'
unexp = int(re.findall(r'\(char (\d+)\)', str(e))[0])
# position of unescaped '"' before that
unesc = s.rfind(r'"', 0, unexp)
s = s[:unesc] + r'\"' + s[unesc+1:]
# position of correspondig closing '"' (+2 for inserted '\')
closg = s.find(r'"', unesc + 2)
s = s[:closg] + r'\"' + s[closg+1:]
print result
您可能需要添加一些额外的检查以防止这种情况以无限循环结束(例如,重复次数最多与字符串中的字符数一样多)。 此外,如果不正确的"
后面实际上跟着逗号,这仍然不起作用,正如@gnibbler 所指出的那样。
更新:这似乎相当现在工作得很好(尽管仍然不完美),即使未转义的 "
后面跟着一个逗号或右括号,在这种情况下,它可能会在此之后收到有关语法错误的投诉(预期的属性名称等)并追溯到最后一个"
。它还会自动转义相应的关闭"
(假设有一个)。
1) 异常的str
是"Expecting , delimiter: line XXX column YYY (char ZZZ)"
,其中ZZZ 是字符串中发生错误的位置。但请注意,此消息可能取决于 Python 的版本、json
模块、操作系统或语言环境,因此可能必须相应地调整此解决方案。
【讨论】:
太棒了。这并不能解决我的所有问题,但这是一个好的开始。 @eandersson 您能否更具体地说明您期望“更完整的解决方案”是什么样子?在哪些情况下我的解决方案不起作用? (我相信有很多,但哪些与您相关?) 我主要指的是一个库来处理这些类型的怪事,但也是一个更灵活的解决方案来解决这种无法处理的极端情况问题。一个例子是带有一个额外双引号的字符串。 顺便说一句,我真的只是想看看那里是否还有其他东西,否则我只会将奖励积分奖励给这个答案。 从 Python 3.5 开始,您可以使用 JsonDecodeError.pos 获取位置。【参考方案2】:如果这正是 API 返回的内容,那么他们的 API 就有问题。这是无效的 JSON。尤其是在这个区域周围:
"ads":
"sponsored_article":
"title": "Образовательный центр "ADVANCE"", <-- here
"text": "Бизнес.Риторика.Английский язык.Подготовка к школе.Подготовка к ЕГЭ."
,
"warning": null
ADVANCE 周围的双引号不会被转义。您可以使用http://jsonlint.com/ 之类的东西来验证它。
这是"
没有被转义的问题,如果这是你得到的,那么数据在源头是坏的。他们需要修复它。
Parse error on line 4:
...азовательный центр "ADVANCE"",
-----------------------^
Expecting '', ':', ',', ']'
这解决了问题:
"title": "Образовательный центр \"ADVANCE\"",
【讨论】:
我需要自动修复这个问题。 你应该首先写信给2gis
,告诉他们他们没有返回正确的JSON。
你可以尝试解析 JSON,如果失败,jsonString.replace('""', '\""')
。这可能会奏效。但它可能会在其他输入上失败。没有正确的解决方案。【参考方案3】:
唯一真正确定的解决方案是要求 2gis 修复他们的 API。
与此同时,可以修复编码错误的 JSON 转义字符串中的双引号。如果每个键值对后跟一个换行符(似乎来自发布的数据),则以下函数将完成这项工作:
def fixjson(badjson):
s = badjson
idx = 0
while True:
try:
start = s.index( '": "', idx) + 4
end1 = s.index( '",\n',idx)
end2 = s.index( '"\n', idx)
if end1 < end2:
end = end1
else:
end = end2
content = s[start:end]
content = content.replace('"', '\\"')
s = s[:start] + content + s[end:]
idx = start + len(content) + 6
except:
return s
请注意一些假设:
该函数尝试转义属于键值对的值字符串中的双引号字符。
假设要转义的文本在序列之后开始
": "
在序列之前结束
",\n
或
"\n
将发布的 JSON 传递给函数会产生此返回值
"api_version": "1.3",
"response_code": "200",
"id": "3237490513229753",
"lon": "38.969916127827",
"lat": "45.069889625267",
"page_url": null,
"name": "ATB",
"firm_group":
"id": "3237499103085728",
"count": "1"
,
"city_name": "Krasnodar",
"city_id": "3237585002430511",
"address": "Turgeneva, 172/1",
"create_time": "2008-07-22 10:02:04 07",
"modification_time": "2013-08-09 20:04:36 07",
"see_also": [
"id": "3237491513434577",
"lon": 38.973110606808,
"lat": 45.029031222211,
"name": "Advance",
"hash": "5698hn745A8IJ1H86177uvgn94521J3464he26763737242Cf6e654G62J0I7878e",
"ads":
"sponsored_article":
"title": "Center \"ADVANCE\"",
"text": "Business.English."
,
"warning": null
]
请记住,如果您的需求不能完全满足,您可以轻松自定义功能。
【讨论】:
【参考方案4】:上面的想法很好,但我有问题。我的 json Sting 只包含一个额外的双引号。 因此,我对上面给出的代码进行了修复。
jsonStr 是
"api_version": "1.3",
"response_code": "200",
"id": "3237490513229753",
"lon": "38.969916127827",
"lat": "45.069889625267",
"page_url": null,
"name": "ATB",
"firm_group":
"id": "3237499103085728",
"count": "1"
,
"city_name": "Krasnodar",
"city_id": "3237585002430511",
"address": "Turgeneva, 172/1",
"create_time": "2008-07-22 10:02:04 07",
"modification_time": "2013-08-09 20:04:36 07",
"see_also": [
"id": "3237491513434577",
"lon": 38.973110606808,
"lat": 45.029031222211,
"name": "Advance",
"hash": "5698hn745A8IJ1H86177uvgn94521J3464he26763737242Cf6e654G62J0I7878e",
"ads":
"sponsored_article":
"title": "Center "ADVANCE",
"text": "Business.English."
,
"warning": null
]
修复方法如下:
import json, re
def fixJSON(jsonStr):
# Substitue all the backslash from JSON string.
jsonStr = re.sub(r'\\', '', jsonStr)
try:
return json.loads(jsonStr)
except ValueError:
while True:
# Search json string specifically for '"'
b = re.search(r'[\w|"]\s?(")\s?[\w|"]', jsonStr)
# If we don't find any the we come out of loop
if not b:
break
# Get the location of \"
s, e = b.span(1)
c = jsonStr[s:e]
# Replace \" with \'
c = c.replace('"',"'")
jsonStr = jsonStr[:s] + c + jsonStr[e:]
return json.loads(jsonStr)
此代码也适用于问题陈述中提到的 JSON 字符串
或者你也可以这样做:
def fixJSON(jsonStr):
# First remove the " from where it is supposed to be.
jsonStr = re.sub(r'\\', '', jsonStr)
jsonStr = re.sub(r'"', '`', jsonStr)
jsonStr = re.sub(r'"', '`', jsonStr)
jsonStr = re.sub(r'":"', '`:`', jsonStr)
jsonStr = re.sub(r'":', '`:', jsonStr)
jsonStr = re.sub(r'","', '`,`', jsonStr)
jsonStr = re.sub(r'",', '`,', jsonStr)
jsonStr = re.sub(r',"', ',`', jsonStr)
jsonStr = re.sub(r'\["', '\[`', jsonStr)
jsonStr = re.sub(r'"\]', '`\]', jsonStr)
# Remove all the unwanted " and replace with ' '
jsonStr = re.sub(r'"',' ', jsonStr)
# Put back all the " where it supposed to be.
jsonStr = re.sub(r'\`','\"', jsonStr)
return json.loads(jsonStr)
【讨论】:
很好,但不是从文本中删除所有"
,为什么不用一些占位符字符替换它们(在字符串中没有找到),然后用转义后的引号替换它们正确的"
已被放回?
这将是一个正确的方法。【参考方案5】:
需要对JSON字符串中的双引号进行转义,如下:
"title": "Образовательный центр \"ADVANCE\"",
要以编程方式修复它,最简单的方法是修改您的 JSON 解析器,以便您了解错误的上下文,然后尝试修复它。
【讨论】:
【参考方案6】:我做了一个 jsonfixer 来解决这样的问题。
It's Python Package (2.7)(一个完成了一半的命令行工具)
看看https://github.com/half-pie/half-json
from half_json.core import JSONFixer
f = JSONFixer(max_try=100)
new_s = s.replace('\n', '')
result = f.fix(new_s)
d = json.loads(result.line)
# u'name': u'ATB', u'modification_time': u'2013-08-09 20:04:36 07', u'city_id': u'3237585002430511', u'see_also': [u'hash': u'5698hn745A8IJ1H86177uvgn94521J3464he26763737242Cf6e654G62J0I7878e', u'ads': u'warning': None, u'sponsored_article': u'ADVANCE': u', ', u'text': u'Business.English.', u'title': u'Center ', u'lon': 38.973110606808, u'lat': 45.029031222211, u'id': u'3237491513434577', u'name': u'Advance'], u'response_code': u'200', u'lon': u'38.969916127827', u'firm_group': u'count': u'1', u'id': u'3237499103085728', u'create_time': u'2008-07-22 10:02:04 07', u'city_name': u'Krasnodar', u'address': u'Turgeneva, 172/1', u'lat': u'45.069889625267', u'id': u'3237490513229753', u'api_version': u'1.3', u'page_url': None
和https://github.com/half-pie/half-json/blob/master/tests/test_cases.py#L76-L80中的测试用例
line = '"title": "Center "ADVANCE"", "text": "Business.English."'
ok, newline, _ = JSONFixer().fix(line)
self.assertTrue(ok)
self.assertEqual('"title": "Center ","ADVANCE":", ","text": "Business.English."', newline)
【讨论】:
【参考方案7】:在https://fix-json.com 的源代码中,我找到了一个解决方案,但它很脏,看起来像个黑客。适应python就行了
jsString.match(/:.*"(.*)"/gi).forEach(function(element)
var filtered = element.replace(/(^:\s*"|"(,)?$)/gi, '').trim();
jsString = jsString.replace(filtered, filtered.replace(/(\\*)\"/gi, "\\\""));
);
【讨论】:
【参考方案8】:它并不完美也不丑,但它对我有帮助
def get_json_info(info_row: str, type) -> dict:
try:
info = json.loads(info_row)
except JSONDecodeError:
data =
try:
for s in info_row.split('","'):
if not s:
continue
key, val = s.split(":", maxsplit=1)
key = key.strip().lstrip("").strip('"')
val: str = re.sub('"', '\\"', val.lstrip('"').strip('\"'))
data[key] = val
except ValueError:
print("ERROR:", info_row)
info = data
return info
【讨论】:
【参考方案9】:修复 #1
如果您从某个网站获取它,请确保您使用的是相同的字符串。就我而言,我正在做 .replace('\\"','"')
。因此,数据不再是 json。如果你也做了什么。像这样,请修复它。
修复 #2
尝试在键名的所有位置添加一些字符。会好的。
【讨论】:
以上是关于如何自动修复无效的 JSON 字符串?的主要内容,如果未能解决你的问题,请参考以下文章
如何修复 Invariant Violation:元素类型无效:预期为字符串(对于内置组件)
如何修复元素类型无效:期望字符串(用于内置组件)或类/函数(用于复合组件)但得到:对象