Pyparsing: Parsowanie semi-JSON zagnieżdżonych danych tekstowych do listy
Mam kilka zagnieżdżonych danych w formacie, który luźno przypomina 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=
{
...
}
Istnieje wiele różnych parametrów o różnych poziomach głębi - jest to bardzo mały podzbiór.
Warto również zauważyć, że gdy tworzona jest nowa pod-tablica, zawsze występuje znak równości, po którym następuje przerwa linii, po której następuje otwarty nawias (jak pokazano powyżej).
Czy istnieje jakaś prosta technika pętli lub rekurencji do konwersji tych danych na format danych przyjazny dla systemu, taki jak tablice lub JSON? Chcę uniknąć kodowania nazw właściwości. Szukam czegoś, co będzie działać w Pythonie, Javie lub PHP. Pseudo-kod też jest w porządku.
Doceniam każdą pomoc.
EDIT: Odkryłem bibliotekę Pyparsing dla Pythona i wygląda na to, że może to być duża pomoc. Nie mogę znaleźć przykładów użycia Pyparsing do analizowania zagnieżdżonych struktur o nieznanej głębokości. Czy ktoś może rzucić światło na Pyparsing pod względem danych, które opisałem powyżej?
EDYCJA 2: Dobra, oto działające rozwiązanie w 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()
To działa, ale kiedy próbuję zapisać wyniki do pliku za pomocą json.dump (wynik), zawartość pliku jest zawijana w podwójne cudzysłowy. Są też\n
znaki między wieloma parami danych. Próbowałem je ukryć w powyższym kodzie za pomocąLineEnd().suppress()
, ale nie mogę go używać poprawnie.
W porządku, wymyśliłem ostateczne rozwiązanie, które faktycznie przekształca te dane w Dict przyjazny dla JSON, jak pierwotnie chciałem. Najpierw za pomocą Pyparsing konwertuje dane na serię zagnieżdżonych list, a następnie przechwytuje listę i przekształca ją w JSON. To pozwala mi przezwyciężyć problem związany z PyparsingiemtoDict()
metoda nie była w stanie obsłużyć miejsca, w którym ten sam obiekt ma dwie właściwości o tej samej nazwie. Aby określić, czy lista jest zwykłą listą, czy parą właściwości / wartości, należyprependPropertyToken
metoda dodaje łańcuch__property__
przed nazwami właściwości, gdy Pyparsing je wykryje.
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]