33

从 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”:“Center “ADVANCE””

如何在 Python 中自动修复它?

4

9 回答 9

44

@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模块、操作系统或语言环境,因此可能必须相应地调整此解决方案。

于 2013-08-29T16:06:15.587 回答
8

如果这正是 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\"",
于 2013-08-29T15:27:26.893 回答
4

唯一真正确定的解决方案是要求 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
            }
        }
    ]
}

请记住,如果您的需求不能完全满足,您可以轻松地自定义功能。

于 2013-11-23T20:46:52.303 回答
3

上面的想法很好,但我有问题。我的 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)
于 2015-09-25T08:03:36.903 回答
2

您需要对 JSON 字符串中的双引号进行转义,如下所示:

"title": "Образовательный центр \"ADVANCE\"",

要以编程方式修复它,最简单的方法是修改 JSON 解析器,以便为错误提供一些上下文,然后尝试修复它。

于 2013-08-29T15:41:07.283 回答
1

我制作了一个 jsonfixer 来解决这样的问题。

它是 Python 包 (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)
于 2019-06-01T14:49:08.627 回答
0

它并不完美和丑陋,但它对我有帮助

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
于 2019-02-02T21:49:53.750 回答
0

修复 #1

如果您从某个网站获取它,请确保您使用的是相同的字符串。就我而言,我正在做.replace('\\"','"'). 因此,数据不再是 json。如果你也做了什么。像这样,请修复它。

修复 #2

尝试在键名的所有位置添加一些字符。没事的。

于 2020-05-28T07:50:17.810 回答
0

https://fix-json.com的来源中,我找到了一个解决方案,但它很脏,看起来像一个 hack。只需将其适应python

jsString.match(/:.*"(.*)"/gi).forEach(function(element){
   var filtered = element.replace(/(^:\s*"|"(,)?$)/gi, '').trim();
   jsString = jsString.replace(filtered, filtered.replace(/(\\*)\"/gi, "\\\""));
});
于 2019-01-02T19:21:00.057 回答