【问题标题】:Python: parsing JSON-like Javascript data structures (w/ consecutive commas)Python:解析类似 JSON 的 Javascript 数据结构(带连续逗号)
【发布时间】:2012-04-20 21:23:40
【问题描述】:

我想解析类似 JSON 的字符串。它们与普通 JSON 的唯一区别是数组中存在 连续逗号。当有两个这样的逗号时,它隐含地意味着null 应该插入到它们之间。示例:

       JSON-like:  ["foo",,,"bar",[1,,3,4]]
      Javascript:  ["foo",null,null,"bar",[1,null,3,4]]
Decoded (Python):  ["foo", None, None, "bar", [1, None, 3, 4]]

本机 json.JSONDecoder 类不允许我更改数组解析的行为。我只能修改对象(字典)、整数、浮点数、字符串的解析器(通过将 kwargs 函数提供给JSONDecoder(),请参阅the doc)。

那么,这是否意味着我必须从头开始编写 JSON 解析器? json 的 Python 代码是可用的,但它相当混乱。我宁愿使用它的内部结构而不是复制它的代码!

【问题讨论】:

标签: javascript python json parsing


【解决方案1】:

由于您尝试解析的不是 JSON本身,而是与 JSON 非常相似的不同语言,因此您可能需要自己的解析器。

幸运的是,这并不像听起来那么难。您可以使用 Python 解析器生成器,例如 pyparsing。 JSON 可以使用相当简单的上下文无关语法来完全指定(我找到了一个 here),因此您应该能够对其进行修改以满足您的需求。

【讨论】:

  • 也许它有点矫枉过正,但其他人可能需要它!所以我要 +1!
  • +1。请注意,虽然我的回答确实为您提供了一些应该起作用的东西(据我所知),但这是一个更好的解决方案。它可能需要更多的工作,但它会更有弹性。如果你正在做一些小事,那么一定要使用我的技巧,但如果你正在做更重要的事情,那就好好做吧。
  • @Lattyware 这绝对是真的。例如,如果字符串对象中有连续的逗号,我们的方法就会搞砸。
  • 请看我的新答案。我几乎得到了我想要的,但在一种情况下仍然失败。
【解决方案2】:

尝试小而简单的解决方法:

  1. 将类似 JSON 的数据转换为字符串。
  2. 将“,”替换为“,null”。
  3. 将其转换为您的表示形式。
  4. 让 JSONDecoder(), 做繁重的工作。

    1. & 3. 如果你已经处理过字符串,可以省略。

(如果转换为字符串不切实际,请使用此信息更新您的问题!)

【讨论】:

  • 谢谢。不过,我已经尝试过这样做。我使用了一个简单的 re.sub,但有时会保留一些 ,null,,。这个解决方法有点太脏了!
  • 不幸的是,它并不像这样简单,因为当您将 ,, 替换为 ,null, 时,您添加了一个逗号,因此您从 ,,, 转到 ,null,, 仍然失败。
  • @Lattyware 但是,如果您使用后视,如my answer,一切都很好。无论如何,它适用于您的示例。 :)
【解决方案3】:

您可以使用后向表达式一次性替换Lattyware's/przemo_li's 答案的逗号,即“替换所有逗号前面的逗号”:

>>> s = '["foo",,,"bar",[1,,3,4]]'

>>> re.sub(r'(?<=,)\s*,', ' null,', s)
'["foo", null, null,"bar",[1, null,3,4]]'

请注意,这适用于您可以假设字符串文字中没有连续逗号的小事情,例如。一般来说,正则表达式不足以解决这个问题,Taymon's approach 使用真正的解析器是唯一完全正确的解决方案。

【讨论】:

  • 我知道必须有一种方法可以完全使用正则表达式来完成它,但可惜,它超出了我的范围。 +1,这是一个更简洁的解决方案。
  • 您的代码需要修复 - re.sub(r'(?&lt;=,)\s*,', ' null,') 应该是 re.sub(r'(?&lt;=,),', ' null,', s)
【解决方案4】:

这是一种 hackish 方式,但一种解决方案是简单地对 JSON-ish 数据进行一些字符串修改,以便在解析之前使其符合要求。

import re
import json

not_quite_json = '["foo",,,"bar",[1,,3,4]]'
not_json = True
while not_json:
    not_quite_json, not_json = re.subn(r',\s*,', ', null, ', not_quite_json)

这给我们留下了:

'["foo", null, null, "bar",[1, null, 3,4]]'

我们可以这样做:

json.loads(not_quite_json)

给我们:

['foo', None, None, 'bar', [1, None, 3, 4]]

请注意,它并不像替换那么简单,因为替换还会插入可能需要替换的逗号。鉴于此,您必须循环访问,直到无法进行更多替换。在这里,我使用了一个简单的正则表达式来完成这项工作。

【讨论】:

    【解决方案5】:

    我查看了 Taymon 推荐,pyparsing,并成功破解了 here 提供的示例以满足我的需求。 它在模拟 Javascript eval() 时效果很好,但 在一种情况下失败:尾随逗号。应该有一个可选的尾随逗号 - 请参阅下面的测试 - 但我找不到任何合适的方法来实现它。

    from pyparsing import *
    
    TRUE = Keyword("true").setParseAction(replaceWith(True))
    FALSE = Keyword("false").setParseAction(replaceWith(False))
    NULL = Keyword("null").setParseAction(replaceWith(None))
    
    jsonString = dblQuotedString.setParseAction(removeQuotes)
    jsonNumber = Combine(Optional('-') + ('0' | Word('123456789', nums)) +
                        Optional('.' + Word(nums)) +
                        Optional(Word('eE', exact=1) + Word(nums + '+-', nums)))
    
    jsonObject = Forward()
    jsonValue = Forward()
    # black magic begins
    commaToNull = Word(',,', exact=1).setParseAction(replaceWith(None))
    jsonElements = ZeroOrMore(commaToNull) + Optional(jsonValue) + ZeroOrMore((Suppress(',') + jsonValue) | commaToNull)
    # black magic ends
    jsonArray = Group(Suppress('[') + Optional(jsonElements) + Suppress(']'))
    jsonValue << (jsonString | jsonNumber | Group(jsonObject) | jsonArray | TRUE | FALSE | NULL)
    memberDef = Group(jsonString + Suppress(':') + jsonValue)
    jsonMembers = delimitedList(memberDef)
    jsonObject << Dict(Suppress('{') + Optional(jsonMembers) + Suppress('}'))
    
    jsonComment = cppStyleComment
    jsonObject.ignore(jsonComment)
    
    def convertNumbers(s, l, toks):
        n = toks[0]
        try:
            return int(n)
        except ValueError:
            return float(n)
    
    jsonNumber.setParseAction(convertNumbers)
    
    def test():
        tests = (
            '[1,2]',       # ok
            '[,]',         # ok
            '[,,]',        # ok
            '[  , ,  , ]', # ok
            '[,1]',        # ok
            '[,,1]',       # ok
            '[1,,2]',      # ok
            '[1,]',        # failure, I got [1, None], I should have [1]
            '[1,,]',       # failure, I got [1, None, None], I should have [1, None]
        )
        for test in tests:
            results = jsonArray.parseString(test)
            print(results.asList())
    

    【讨论】:

    • 代替双逗号字符串的黑魔法,你能简单地让空字符串成为列表中的有效元素吗?这对我来说似乎更干净,并且可以在列表末尾正常工作。
    • 等等,抱歉,我误解了这个问题。所以你想从每个有一个列表的末尾砍掉一个尾随隐式null
    • 我认为您的选择就是这样做(解析字符串后,如果有的话,从列表中删除尾随 null;您还必须确保它是隐含的而不是字面的),或者明确允许在列表末尾使用尾随逗号,然后使用优先级来防止歧义。
    • 嗯,你可能是对的,但我很难理解这个 pyparsing 的东西是如何工作的......
    【解决方案6】:

    对于那些寻求快速而肮脏的东西来转换一般 JS 对象(到字典)的人。一个真实站点的页面的某些部分给了我一些我想处理的对象。日期有“新”结构,它在一行中,中间没有空格,所以两行就足够了:

    data=sub(r'new Date\(([^)])*\)', r'\1', data)
    data=sub(r'([,{])(\w*):', r'\1"\2":', data)
    

    然后 json.loads() 工作正常。您的里程可能会有所不同:)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-01-15
      • 2019-03-09
      • 1970-01-01
      • 1970-01-01
      • 2014-02-26
      相关资源
      最近更新 更多