我想解析类似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
类不允许我更改数组解析的行为。我只能修改对象(dicts),整数,浮点数,字符串的解析器(通过将kwargs函数赋予JSONDecoder()
,请参阅the doc)。
那么,这是否意味着我必须从头开始编写JSON解析器? json
的Python代码可用,但它非常混乱。我宁愿使用它的内部而不是复制它的代码!
答案 0 :(得分:5)
由于您要解析的内容不是JSON 本身,而是一种非常类似于JSON的不同语言,您可能需要自己的解析器。
幸运的是,这并不像听起来那么难。您可以使用像pyparsing这样的Python解析器生成器。可以使用相当简单的无上下文语法完全指定JSON(我发现了一个here),因此您应该能够根据自己的需要对其进行修改。
答案 1 :(得分:3)
小&尝试简单的解决方法:
让JSONDecoder(), 做重物。
(如果转换为字符串是不切实际的,请使用此信息更新您的问题!)
答案 2 :(得分:2)
您可以使用lookbehind表达式在一次传递中以逗号替换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使用真正的解析器是唯一完全正确的解决方案。
答案 3 :(得分:1)
这是一种执行它的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]]
请注意,它不像替换那么简单,因为替换也会插入可能需要替换的逗号。鉴于此,您必须循环,直到不能再进行替换。在这里,我使用了一个简单的正则表达式来完成这项工作。
答案 4 :(得分:1)
我已经看过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())
答案 5 :(得分:0)
对于那些寻求快速而又肮脏的东西来转换一般JS对象(到字典)的人。一个真实站点页面的某些部分为我提供了一些我想解决的对象。日期有“新”结构,它在一行中,中间没有空格,所以两行就足够了:
data=sub(r'new Date\(([^)])*\)', r'\1', data)
data=sub(r'([,{])(\w*):', r'\1"\2":', data)
然后json.loads()工作正常。您的里程可能会有所不同:)