python loads可以忽略后面的逗号吗?

fd3cxomn  于 2023-01-19  发布在  Python
关注(0)|答案(7)|浏览(111)

正如this StackOverflow question中提到的,json中不允许有任何尾随逗号。

{
    "key1": "value1",
    "key2": "value2"
}

很好,但是这个

{
    "key1": "value1",
    "key2": "value2",
}

是无效语法。
由于this other StackOverflow question中提到的原因,在Python代码中使用尾随逗号是法律的的(也许是鼓励的?)。我同时使用Python和JSON,所以我希望能够在两种类型的文件中保持一致。有没有办法让json.loads忽略尾随逗号?

zpqajqem

zpqajqem1#

快进到2021年,现在我们有了https://pypi.org/project/json5/
引用链接:
JSON5数据格式的Python实现。
JSON5扩展了JSON数据交换格式,使其作为配置语言更易于使用:

  • JavaScript风格的注解(单行和多行)都是法律的的。
  • 如果对象键是法律的的ECMAScript标识符,则可以不加引号
  • 对象和数组可以以尾随逗号结尾。
  • 字符串可以用单引号引起来,并且允许使用多行字符串文字。

用法与python内置的json模块一致:

>>> import json5
>>> json5.loads('{"key1": "{my special value,}",}')
{u'key1': u'{my special value,}'}

它确实有一个警告:

已知问题

  • 我有没有提过它很慢?

加载启动配置等足够快。

kyvafyod

kyvafyod2#

可以用jsoncomment Package python的json解析器
JSON注解允许解析JSON文件或字符串:

  • 单行和多行注解
  • 多行数据字符串
  • 对象和数组中最后一项之后的尾随逗号

示例用法:

import json
from jsoncomment import JsonComment

with open(filename) as data_file:    
    parser = JsonComment(json)
    data = parser.load(data_file)
ltskdhd1

ltskdhd13#

在传入值之前去掉逗号。

import re

def clean_json(string):
    string = re.sub(",[ \t\r\n]+}", "}", string)
    string = re.sub(",[ \t\r\n]+\]", "]", string)

    return string
n3h0vuf2

n3h0vuf24#

在python中,你可以在字典和列表中使用尾随逗号,所以我们应该可以使用ast.literal_eval来利用这一点:

import ast, json

str = '{"key1": "value1", "key2": "value2",}'

python_obj = ast.literal_eval(str) 
# python_obj is {'key1': 'value1', 'key2': 'value2'}

json_str = json.dumps(python_obj)
# json_str is '{"key1": "value1", "key2": "value2"}'

然而,JSON并不是“精确的”python,因此存在一些边缘情况。例如,像 null,true,false 这样的值在python中并不存在。我们可以在运行eval之前用有效的python等效值替换它们:

import ast, json

def clean_json(str):
  str = str.replace('null', 'None').replace('true', 'True').replace('false', 'False')
  return json.dumps(ast.literal_eval(str))

不幸的是,这会损坏任何包含单词 null、true或false 的字符串。

{"sentence": "show your true colors"}

会变成

{"sentence": "show your True colors"}
iyfjxgzm

iyfjxgzm5#

我综合了其他几个答案的知识,特别是使用@Porkbutts答案中的literal_eval的想法,提出了一个非常邪恶的解决方案

def json_cleaner_loader(path):
    with open(path) as fh:
        exec("null=None;true=True;false=False;d={}".format(fh.read()))
    return locals()["d"]

在将JSON结构体作为Python代码求值之前,将缺少的常量定义为Python值,这样就可以从locals()(另一个字典)访问该结构体。
这应该适用于Python 2.7和Python 3.x

小心这将执行传递文件中的任何内容,这可能会做Python解释器所能做的任何事情,所以它应该只用于已知安全的输入(即,不要让Web客户端提供内容),并且可能不在任何生产环境中使用。

如果它被赋予了大量的内容,这可能也会失败。
后期增编:这种(糟糕的)方法的一个副作用是,它支持JSON(类似JSON?)数据中的Python注解,尽管很难将其与友好的非标准行为进行比较。

guykilcj

guykilcj6#

使用rapidjson

rapidjson.load("file.json", parse_mode = rapidjson.PM_COMMENTS | rapidjson.PM_TRAILING_COMMAS)
1rhkuytd

1rhkuytd7#

如果我没有使用任何外部模块的选项,我的典型方法是首先对输入进行净化(即删除结尾的逗号和注解),然后使用内置的JSON解析器。
下面的示例使用三个正则表达式来去除单行和多行注解,然后去除JSON输入字符串上的尾随逗号,再将其传递给内置的json.loads方法。

#!/usr/bin/env python

import json, re, sys

unfiltered_json_string = '''
{
    "name": "Grayson",
    "age": 45,
    "car": "A3",
    "flag": false,
    "default": true,
    "entries": [ // "This is the beginning of the comment with some quotes" """""
        "red", // This is another comment. " "" """ """"
        null, /* This is a multi line comment //
"Here's a quote on another line."
*/
        false,
        true,
    ],
    "object": {
        "key3": null,
        "key2": "This is a string with some comment characters // /* */ // /////.",
        "key1": false,
    },
}
'''

RE_SINGLE_LINE_COMMENT = re.compile(r'("(?:(?=(\\?))\2.)*?")|(?:\/{2,}.*)')
RE_MULTI_LINE_COMMENT = re.compile(r'("(?:(?=(\\?))\2.)*?")|(?:\/\*(?:(?!\*\/).)+\*\/)', flags=re.M|re.DOTALL)
RE_TRAILING_COMMA = re.compile(r',(?=\s*?[\}\]])')

if sys.version_info < (3, 5):
    # For Python versions before 3.5, use the patched copy of re.sub.
    # Based on https://gist.github.com/gromgull/3922244
    def patched_re_sub(pattern, repl, string, count=0, flags=0):
        def _repl(m):
            class _match():
                def __init__(self, m):
                    self.m=m
                    self.string=m.string
                def group(self, n):
                    return m.group(n) or ''
            return re._expand(pattern, _match(m), repl)
        return re.sub(pattern, _repl, string, count=0, flags=0)
    filtered_json_string = patched_re_sub(RE_SINGLE_LINE_COMMENT, r'\1', unfiltered_json_string)
    filtered_json_string = patched_re_sub(RE_MULTI_LINE_COMMENT, r'\1', filtered_json_string)
else:
    filtered_json_string = RE_SINGLE_LINE_COMMENT.sub(r'\1', unfiltered_json_string)
    filtered_json_string = RE_MULTI_LINE_COMMENT.sub(r'\1', filtered_json_string)
filtered_json_string = RE_TRAILING_COMMA.sub('', filtered_json_string)

json_data = json.loads(filtered_json_string)
print(json.dumps(json_data, indent=4, sort_keys=True))

相关问题