pyparsing: 示例JSON解析器无法处理字典列表

3

大家好,

我正在尝试使用pyparsing处理一个字典列表。我回到了JSON解析器的示例来获取最佳实践,但是我发现它也无法处理字典列表!

考虑以下情况(这是默认JSON解析器的示例,但删除了一些注释并提供了我的测试用例):

#!/usr/bin/env python2.7

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()
jsonElements = delimitedList( jsonValue )
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, ve:
        return float(n)

jsonNumber.setParseAction( convertNumbers )

if __name__ == "__main__":
    testdata = """
[ { "foo": "bar", "baz": "bar2" },
  { "foo": "bob", "baz": "fez" } ]
    """
    results = jsonValue.parseString(testdata)
    print "[0]:", results[0].dump()
    print "[1]:", results[1].dump()

以下是有效的JSON格式,但使用pyparsing示例尝试索引第二个预期数组元素时会失败:

[0]: [[['foo', 'bar'], ['baz', 'bar2']], [['foo', 'bob'], ['baz', 'fez']]]
[1]:
Traceback (most recent call last):
  File "json2.py", line 42, in <module>
    print "[1]:", results[1].dump()
  File "/Library/Python/2.7/site-packages/pyparsing.py", line 317, in __getitem__
    return self.__toklist[i]
IndexError: list index out of range

有人能帮我找出这个语法错误吗?

编辑:修复了尝试将其解析为JSON对象而不是值的错误。

注意:这与pyparsing:用于字典列表的语法(erlang)相关,我基本上正在尝试使用Erlang数据结构执行相同的操作,并且以类似的方式失败:(

2个回答

3

这可能是有效的JSON,但是你的语法无法处理它。原因如下:

jsonObject << Dict( Suppress('{') + Optional(jsonMembers) + Suppress('}') )

这句话的意思是语法对象必须用{...}括起来。而你把它作为一个数组[...]进行了处理。由于最顶层的对象必须是一个字典,因此需要键名。将你的测试数据更改为:
{ "col1":{ "foo": "bar", "baz": "bar2" },
  "col2":{ "foo": "bob", "baz": "fez" } }

或者

{ "data":[{ "foo": "bar", "baz": "bar2" },
          { "foo": "bob", "baz": "fez" }] }

这将允许此语法进行解析。想要将顶层对象设置为数组?只需修改语法!


感谢您发现我的错误,我尝试将其解析为对象而不是一般值,但仍无法在原始形式中解析(我实际上无法更改JSON的结构)。我想修改语法以处理字典列表,但我不知道问题出在哪里! - DaveR
@DaveRigby 我现在无法测试,但它看起来应该像这样:jsonObject << Dict( ((Suppress('{') + Optional(jsonMembers) + Suppress('}')) | jsonArray ) - Hooked
是的,所以我更新了上面的测试代码,尝试将其解析为 jsonValue - 这样解析不会出现异常,但是没有给我正确的字典列表 - 请参见上面。 - DaveR

2

当使用该表达式后,您会得到一个解析结果对象,其中包含匹配到的令牌列表 - pyparsing无法知道您是否要匹配一个或多个令牌,因此它会返回一个列表,对于您的情况,该列表仅包含一个元素,即字典数组。

更改:

results = jsonValue.parseString(testdata)

为了

results = jsonValue.parseString(testdata)[0]

我认为事情会开始变得更好。做完这个之后,我得到了:

[0]: [['foo', 'bar'], ['baz', 'bar2']]
- baz: bar2
- foo: bar
[1]: [['foo', 'bob'], ['baz', 'fez']]
- baz: fez
- foo: bob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接