【问题标题】:Python: JSON decoding library which can associate decoded items with original line number?Python:可以将解码项目与原始行号相关联的 JSON 解码库?
【发布时间】:2011-11-05 16:56:33
【问题描述】:

我经常将 JSON 用于用户可编辑的配置文件。 json.loads 当然会为我提取格式错误的 JSON,但有时会出现一些错误,直到我检查生成的 dicts/lists/strings。我希望能够给出有用的错误,例如“第 23 行的无效值 'foo'”,但是当我取回我的 dict 时,我丢失了与原始行号的任何映射。

似乎有人可能编写了一个 JSON 解析器,它用一些元数据标记每个输出对象,这些元数据是关于它在输入文本中出现的位置:python 是否存在这样的东西?

示例:

1. [{"foo": "x"},
2.  {"bar": "y"}]

经过上面的解析,我发现“y”实际上不是“bar”的合法值,我想知道它来自第2行。

【问题讨论】:

  • 不幸的是我相信 Python 没有这样的库,或者至少我在主要的 JSON 库(json、simplejson、Jansson)中没有看到这个功能

标签: python json


【解决方案1】:

AFAIK 你想要的东西不存在,但如果你有兴趣,我知道你可以如何实现它......

json 模块有一个用于解码对象的钩子,您可以(错误地)使用它来进行解码时对象验证。但是,这不会解决您的问题,因为钩子没有获取行号信息。这个问题更加复杂,因为您在 Python 2.7+ 中不再收到逐行错误消息。您只能从纯 Python JSON 解码器中获取它们,而较新的版本使用(快得多)C 库。

所以我们有两个问题要解决。

1) 你可以像这样子类化 json.JSONDecoder 来使用纯 Python 解码器:

class PyDecoder(json.JSONDecoder):
    def __init__(self, encoding=None, object_hook=None, parse_float=None,
            parse_int=None, parse_constant=None, strict=True,
            object_pairs_hook=None):
        super(PyDecoder, self).__init__(encoding, object_hook, parse_float,
                                        parse_int, parse_constant, strict)
        self.scan_once = json.scanner.py_make_scanner(self)

2) 要获得您的验证,您需要将 json.decoder.JSONObject 替换为一个做几乎相同的事情的方法,但还将行号信息传递给您的验证例程。

【讨论】:

    【解决方案2】:

    完全披露:我是以下软件包的维护者。

    现在有一个新的 Python 包可以解决这个用例:https://github.com/open-alchemy/json-source-map

    安装:pip install json_source_map

    例如,在您的情况下:

    from json_source_map import calculate
    
    source = '''[{"foo": "x"},
    {"bar": "y"}]'''
    
    print(calculate(source))
    

    打印出来:

    {
        "": Entry(
            value_start=Location(line=0, column=0, position=0),
            value_end=Location(line=1, column=13, position=28),
            key_start=None,
            key_end=None,
        ),
        "/0": Entry(
            value_start=Location(line=0, column=1, position=1),
            value_end=Location(line=0, column=13, position=13),
            key_start=None,
            key_end=None,
        ),
        "/0/foo": Entry(
            value_start=Location(line=0, column=9, position=9),
            value_end=Location(line=0, column=12, position=12),
            key_start=Location(line=0, column=2, position=2),
            key_end=Location(line=0, column=7, position=7),
        ),
        "/1": Entry(
            value_start=Location(line=1, column=0, position=15),
            value_end=Location(line=1, column=12, position=27),
            key_start=None,
            key_end=None,
        ),
        "/1/bar": Entry(
            value_start=Location(line=1, column=8, position=23),
            value_end=Location(line=1, column=11, position=26),
            key_start=Location(line=1, column=1, position=16),
            key_end=Location(line=1, column=6, position=21),
        ),
    }
    

    这会告诉您每个键和值在原始 JSON 文档中的确切位置开始和结束。例如,它告诉您"y" 在第 1 行(行为零索引)并从第 8 列开始,到第 11 列结束。

    【讨论】:

      【解决方案3】:

      如果您使用json.load() 并传入一个打开的文件句柄,您收到的任何错误消息都会有一个行号和列号。如果异常是ValueError,则关联的消息应该适合转发给用户。

      【讨论】:

      • 当然,我已经收到了关于 JSON 级别语法错误的好消息。问题是关于处理我在检查结果对象之前没有发现的错误。
      • 抱歉,问题误解了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-02-13
      • 1970-01-01
      • 1970-01-01
      • 2011-07-21
      • 2016-11-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多