【问题标题】:How to parse a string that looks like JSON with lots of embedded classes in python?如何解析一个看起来像 JSON 的字符串,在 python 中有很多嵌入的类?
【发布时间】:2016-04-18 07:16:22
【问题描述】:

我有一个列出请求事件属性的字符串。

我的字符串看起来像:

requestBody: {
    propertyA = 1
    propertyB = 2
    propertyC = {
        propertyC1 = 1
        propertyC2 = 2
    }
    propertyD = [
        { propertyD1 = { propertyD11 = 1}},
        { propertyD1 = [ {propertyD21 = 1, propertyD22 = 2}, 
                         {propertyD21 = 3, propertyD22 = 4}]}
    ]
}

我尝试将"=" 替换为":",以便将其放入python 中的JSON 阅读器中,但JSON 还要求将键和值存储在带有双引号和"," 的字符串中分开每个 KV 对。然后这变得有点复杂。有什么更好的方法可以将其解析为具有完全相同结构的 python 字典(例如,还保留嵌入式字典)?

问题: 如果我要编写一个完整的解析器,我应该解决的主要模式是什么?将括号存储在堆栈中直到括号完成?

【问题讨论】:

  • 它似乎是一致定义的。从头开始编写一个完整的解析器?
  • 我理解将{} 中的多个属性定义为某种嵌套对象(例如propertyC),并且我理解[] 中的多个对象作为对象数组(如propertyD2)。但是,当您在 [] 中具有多个属性时(如 propertyD),您的意图是什么?这真的应该是 {} 中的一个对象,具有 propertyD1propertyD2 属性吗?
  • 此外,有时列表元素似乎是逗号分隔的,有时是换行符分隔的。比如propertyA的定义后面应该有逗号吗?
  • @PaulMcGuire 我稍微修改了我的字符串格式。对于您的第一个问题,对象总是用 {} 括起来,数组应该只包含用 {} 括起来的对象。对于您的第二个问题,是的,对象中逗号分隔和换行符分隔的记录不一致(但是,数组始终以逗号分隔)。
  • 生成这些字符串的软件是什么?可能已经有一个 Python 模块可以使用它。

标签: python json parsing


【解决方案1】:

这是使用 pyparsing 的一个很好的例子,特别是因为它增加了递归结构的问题。

简短的回答是以下解析器(处理前导 requestBody : 之后的所有内容):

LBRACE,RBRACE,LBRACK,RBRACK,EQ = map(Suppress, "{}[]=")
NL = LineEnd().setName("NL")

# define special delimiter for lists and objects, since they can be
# comma-separated or just newline-separated
list_delim = NL | ','
list_delim.leaveWhitespace()

# use a parse action to convert numeric values to ints or floats at parse time
def convert_number(t):
    try:
        return int(t[0])
    except ValueError:
        return float(t[0])
number = Word(nums, nums+'.').addParseAction(convert_number)

qs = quotedString

# forward-declare value, since it will be defined recursively
obj_value = Forward()

ident = Word(alphas, alphanums+'_')
obj_property = Group(ident + EQ + obj_value)

# use Dict wrapper to auto-define nested properties as key-values
obj = Group(LBRACE + Dict(Optional(delimitedList(obj_property, delim=list_delim))) + RBRACE)

obj_array = Group(LBRACK + Optional(delimitedList(obj, delim=list_delim)) + RBRACK)

# now assign to previously-declared obj_value, using '<<=' operator
obj_value <<= obj_array | obj | number | qs

# parse the data
res = obj.parseString(sample)[0]

# convert the result to a dict
import pprint
pprint.pprint(res.asDict())

给予

{'propertyA': 1,
 'propertyB': 2,
 'propertyC': {'propertyC1': 1, 'propertyC2': 2},
 'propertyD': {'propertyD1': {'propertyD11': 1},
               'propertyD2': {'propertyD21': 3, 'propertyD22': 4}}}

【讨论】:

  • 你能简单解释一下obj_value &lt;&lt;= obj_array | obj | number | qs的作用吗?
  • 我们必须使用&lt;&lt;= 运算符将表达式分配给 Forward 表达式。您将在大多数递归 pyparsing 程序中看到这一点。在您的情况下,obj_value 被定义为 Forward 作为一种占位符,就好像在说,“我稍后会定义它,但我需要这个表达式的引用。”然后obj_value 被用于obj_property,它是obj 的一部分。最终,我们想要定义进入obj_value 的内容,它是obj_arrayobj、数字或带引号的字符串中的任何一个。由于obj_value 已经定义为转发,我们使用&lt;&lt;= 运算符分配它。
猜你喜欢
  • 2010-10-28
  • 2021-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多