【问题标题】:Python: check if string is JSON without raising an exception?Python:检查字符串是否为 JSON 而不引发异常?
【发布时间】:2017-07-21 07:12:57
【问题描述】:

我有一个字符串流,我需要分析每个字符串并检查它是否是有效的 JSON。 pythonic 方式 (EAFP) 规定如下内容:

import json
def parse_json(string):
    try:
        return json.loads(string)
    except:
        return string

问题在于大量字符串不是 JSON,并且此代码引发的许多异常会大大减慢该过程。

我正在寻找某种方法来尝试将文本解析为 JSON,返回某种预定义值(例如空的 tuple()),表明该字符串与 JSON 不兼容。 如果这是最简单的解决方案,我不介意绕过标准 json 包(覆盖一两个函数……)。

有什么建议吗?

更新: 因为我只对“复杂”的 JSON(数组和对象)感兴趣,所以我最终决定用一个简单的 if 来检查字符串的第一个和最后一个字符:

try:
    import ujson as json
except ImportError:
    import json


def parse_json(string):
    if len(text) > 0:
        text = text.strip()
        if text != "" and ((text[0] == "{" and text[-1] == "}") or (text[0] == "[" and text[-1] == "]")):
            try:
                return json.loads(string)
            except:
                return string

ujson 是比 Python 的标准 json 更高效的实现。此外,跳过所有未用 [] 或 {} 包裹的字符串可以大大减少异常数量。事实证明,我需要混合 LBYL 和 EAFP。

【问题讨论】:

  • 我非常怀疑开销在于捕获异常,而是首先尝试解析字符串。
  • 正如@Danield 指出的那样,try/except 处理在 Python 中相对便宜。检查一个字符串是否是有效的 JSON 然后转换它可能比你正在做的要慢。但是,如果无效的都以相同的方式损坏,您也许可以避免尝试使用loads() 转换它们,前提是检查该条件的方法非常快。
  • @DanielRoseman 虽然 try-except 子句在没有引发异常时实际上是免费的,但否则代价高昂(例如stackoverflow.com/a/2522013/4369617)。由于我每天必须处理数亿个字符串,这成为一种负担。
  • @marineau 有趣的方法。我会分析最常见的异常原因是什么,并相应地检查它们????????

标签: python json performance parsing exception


【解决方案1】:

更紧凑的方式应该是这样的。 Json lib 只处理 str、bytes 或 bytearray 结构,所以只考虑它们。与if len(text)==0 相比,if not text 对于长字符串要快得多,我们不想知道文本的长度。 Json lib 可能会引发 JsonDecoderError。可以使用正则表达式检查文本的最后一个字符,但我尝试了可能的边缘情况,例如“{]”和“[}”,它们不会失败。

def is_json(text: str) -> bool:
    from json import loads, JSONDecodeError
 
    if not isinstance(text, (str, bytes, bytearray)):
        return False
    if not text:
        return False
    text = text.strip()
    if text[0] in {'{', '['} and text[-1] in {'}', ']'}:
        try:
            loads(text)
        except (ValueError, TypeError, JSONDecodeError):
            return False
        else:
            return True
    else:
        return False

编辑:我们应该检查文本是否为空,而不是引发 IndexError。

def is_json(text: str) -> bool:
    if not isinstance(text, (str, bytes, bytearray)):
        return False
    if not text:
        return False
    text = text.strip()
    if text:
        if text[0] in {'{', '['} and text[-1] in {'}', ']'}:
            try:
                loads(text)
            except (ValueError, TypeError, JSONDecodeError):
                return False
            else:
                return True
        else:
            return False
    return False

【讨论】:

    【解决方案2】:

    对于速度问题,您可以使用多处理来加快解析速度,例如,通过在多处理中使用 5 个工作人员,您一次解析 5 个字符串而不是一个

    如果我理解问题的第二部分,对于自定义异常,请执行以下操作以获取由 json 返回的不同错误的自定义错误:

    def parse_json(string):
        try:
            return json.loads(string)
        except exception1 as ex1:
            print(ex1)
            return string
        except exception2 as ex2:
            print(ex2)
            return string
        exc....
    

    或者如果您不想在引发错误时遇到任何异常,请执行此操作,只需允许它什么都不做,不显示任何内容:

    def parse_json(string):
    
    try:
        return json.loads(string)
    except exception1 as ex1:
        pass
    

    【讨论】:

    • 多/分布式处理是一个选项,但它不会减少我想卸载的异常的额外处理时间。理想情况下,我希望 json.loads() 函数在解析失败时返回一个预定义的值,而不是引发异常。
    【解决方案3】:

    如果字符串是 JSON,它应该以“{”开头并以“}”结尾。

    我认为您可以先检查字符串是否以“{”开头;如果不是,你肯定知道这是一个简单的字符串;如果它以“{”开头,您可以解析它并使用try/except(只是为了确保它不仅仅是一个以“{”开头的简单字符串)。

    【讨论】:

    • 并非所有有效的 JSON 值都是对象。
    • 错误!但我们感谢您的努力
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-10-04
    • 2022-11-28
    • 1970-01-01
    • 1970-01-01
    • 2013-02-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多