【发布时间】:2013-12-19 20:32:39
【问题描述】:
我有一堆嵌套的数据,格式大致类似于 JSON:
company="My Company"
phone="555-5555"
people=
{
person=
{
name="Bob"
location="Seattle"
settings=
{
size=1
color="red"
}
}
person=
{
name="Joe"
location="Seattle"
settings=
{
size=2
color="blue"
}
}
}
places=
{
...
}
有许多具有不同深度级别的不同参数——这只是一个非常小的子集。
还可能值得注意的是,当创建一个新的子数组时,总是有一个等号,后跟一个换行符,然后是左括号(如上所示)。
是否有任何简单的循环或递归技术可以将此数据转换为系统友好的数据格式,例如数组或 JSON?我想避免对属性名称进行硬编码。我正在寻找可以在 Python、Java 或 PHP 中使用的东西。伪代码也可以。
感谢您的帮助。
编辑:我发现了 Python 的 Pyparsing 库,它看起来很有帮助。我找不到任何关于如何使用 Pyparsing 解析未知深度的嵌套结构的示例。任何人都可以根据我上面描述的数据阐明 Pyparsing 吗?
编辑 2:好的,这是 Pyparsing 中的一个有效解决方案:
def parse_file(fileName):
#get the input text file
file = open(fileName, "r")
inputText = file.read()
#define the elements of our data pattern
name = Word(alphas, alphanums+"_")
EQ,LBRACE,RBRACE = map(Suppress, "={}")
value = Forward() #this tells pyparsing that values can be recursive
entry = Group(name + EQ + value) #this is the basic name-value pair
#define data types that might be in the values
real = Regex(r"[+-]?\d+\.\d*").setParseAction(lambda x: float(x[0]))
integer = Regex(r"[+-]?\d+").setParseAction(lambda x: int(x[0]))
quotedString.setParseAction(removeQuotes)
#declare the overall structure of a nested data element
struct = Dict(LBRACE + ZeroOrMore(entry) + RBRACE) #we will turn the output into a Dictionary
#declare the types that might be contained in our data value - string, real, int, or the struct we declared
value << (quotedString | struct | real | integer)
#parse our input text and return it as a Dictionary
result = Dict(OneOrMore(entry)).parseString(inputText)
return result.dump()
这可行,但是当我尝试使用 json.dump(result) 将结果写入文件时,文件的内容用双引号引起来。此外,许多数据对之间还有\n 字符。我尝试在上面的代码中使用LineEnd().suppress() 抑制它们,但我一定没有正确使用它。
好的,我想出了一个最终解决方案,该解决方案实际上将这些数据转换为我最初想要的 JSON 友好的 Dict。它首先使用 Pyparsing 将数据转换为一系列嵌套列表,然后循环遍历列表并将其转换为 JSON。这让我可以克服 Pyparsing 的 toDict() 方法无法处理同一对象具有两个同名属性的问题。为了确定列表是普通列表还是属性/值对,prependPropertyToken 方法会在 Pyparsing 检测到属性名称前添加字符串 __property__。
def parse_file(self,fileName):
#get the input text file
file = open(fileName, "r")
inputText = file.read()
#define data types that might be in the values
real = Regex(r"[+-]?\d+\.\d*").setParseAction(lambda x: float(x[0]))
integer = Regex(r"[+-]?\d+").setParseAction(lambda x: int(x[0]))
yes = CaselessKeyword("yes").setParseAction(replaceWith(True))
no = CaselessKeyword("no").setParseAction(replaceWith(False))
quotedString.setParseAction(removeQuotes)
unquotedString = Word(alphanums+"_-?\"")
comment = Suppress("#") + Suppress(restOfLine)
EQ,LBRACE,RBRACE = map(Suppress, "={}")
data = (real | integer | yes | no | quotedString | unquotedString)
#define structures
value = Forward()
object = Forward()
dataList = Group(OneOrMore(data))
simpleArray = (LBRACE + dataList + RBRACE)
propertyName = Word(alphanums+"_-.").setParseAction(self.prependPropertyToken)
property = dictOf(propertyName + EQ, value)
properties = Dict(property)
object << (LBRACE + properties + RBRACE)
value << (data | object | simpleArray)
dataset = properties.ignore(comment)
#parse it
result = dataset.parseString(inputText)
#turn it into a JSON-like object
dict = self.convert_to_dict(result.asList())
return json.dumps(dict)
def convert_to_dict(self, inputList):
dict = {}
for item in inputList:
#determine the key and value to be inserted into the dict
dictval = None
key = None
if isinstance(item, list):
try:
key = item[0].replace("__property__","")
if isinstance(item[1], list):
try:
if item[1][0].startswith("__property__"):
dictval = self.convert_to_dict(item)
else:
dictval = item[1]
except AttributeError:
dictval = item[1]
else:
dictval = item[1]
except IndexError:
dictval = None
#determine whether to insert the value into the key or to merge the value with existing values at this key
if key:
if key in dict:
if isinstance(dict[key], list):
dict[key].append(dictval)
else:
old = dict[key]
new = [old]
new.append(dictval)
dict[key] = new
else:
dict[key] = dictval
return dict
def prependPropertyToken(self,t):
return "__property__" + t[0]
【问题讨论】:
-
pyparsing wiki 的示例页面包含许多递归结构的示例 - 查找标有“螺旋”图标的示例。
-
谢谢,我没有注意到大部分例子,因为我错误地认为只有开发中和用户贡献的例子。
-
为什么要在解析表达式中添加
Optional(NL)? pyparsing 的主要功能之一是它在 pyparsing 期间自动跳过空格,其中包括换行符。这就是为什么你看不到+ Optional(White())在整个解析器中乱扔垃圾的原因,不像你必须通过正则表达式来处理\s*来处理可能出现空白的地方。result不是字典,即使您可以像访问它一样访问它 - 它是 ParseResults 对象,所以json.dump(result)可能不会做您想做的事。但是就像有一个 asXML 方法一样,您可以尝试编写 asJSON。 -
嗯,好吧,我将摆脱 Optional(NL) 的东西,用 AsJSON 试试你所说的。
标签: python json parsing pyparsing plaintext