2016-04-17 6 views
0

Ich habe eine Zeichenfolge, die die Eigenschaften eines Anforderungsereignisses auflistet.Wie analysiert man einen String, der wie JSON aussieht, mit vielen eingebetteten Klassen in Python?

Meine Zeichenfolge wie folgt aussehen:

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

ich versucht habe, die "=" mit ":" so zu ersetzen, dass ich es in einen JSON-Reader in Python setzen kann, aber JSON erfordert auch, dass Schlüssel und Wert in String gespeichert werden mit doppelten Anführungszeichen und einem ",", um jedes KV-Paar zu trennen. Dies wurde dann ein wenig kompliziert zu implementieren. Was sind einige bessere Ansätze für die Analyse dieses in Python-Wörterbuch mit genau der gleichen Struktur (z. B. eingebettete Wörterbücher sind ebenfalls erhalten)?

Frage: Wenn ich einen vollständigen Parser schreiben würde, was ist das Hauptmuster, das ich angehen sollte? Klammern in einem Stapel speichern, bis die Klammer vollständig ist?

+4

Es scheint konsequent definiert. Schreiben Sie einen vollständigen Parser von Grund auf neu? – usr2564301

+0

Ich verstehe, mehrere Eigenschaften innerhalb '{}' 's als eine Art verschachteltes Objekt (wie' propertyC') zu definieren, und ich verstehe mehrere Objekte innerhalb '[]' s als ein Array von Objekten (wie in 'propertyD2'). Aber was ist beabsichtigt, wenn Sie mehrere Eigenschaften innerhalb von [] haben (wie in 'propertyD')? Sollte dies wirklich ein Objekt in {} 's sein, mit den Eigenschaften 'propertyD1' und' propertyD2'? – PaulMcG

+1

Es scheint auch, dass Listenelemente manchmal durch Kommas getrennt und manchmal durch Zeilentrennzeichen getrennt sind. Zum Beispiel soll es nach der Definition von 'propertyA' ein Komma geben? – PaulMcG

Antwort

2

Dies ist ein schöner Fall für die Verwendung von Pyparssing, besonders da es das Problem der rekursiven Strukturierung hinzufügt.

Die kurze Antwort ist folgende Parser (alles nach dem führenden requestBody : verarbeitet):

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()) 

gibt

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

können Sie kurz erklären, was 'obj_value << = obj_array | obj | Nummer | qs' tut? –

+0

Wir müssen den '<< =' Operator verwenden, um einem Forward-Ausdruck Ausdrücke zuzuweisen. Sie werden dies in den meisten rekursiven Pypar-Programmen sehen. In Ihrem Fall wurde "obj_value" als eine Art Platzhalter definiert, so als wollte ich sagen: "Ich definiere das später, aber ich brauche eine Referenz für diesen Ausdruck." Dann wird 'obj_value' in' obj_property' verwendet, was ein Teil von 'obj' ist. Letztendlich wollen wir definieren, was genau in 'obj_value' steht, was ein' obj_array', ein 'obj', eine Zahl oder ein String in Anführungszeichen ist. Da 'obj_value' bereits als Forward definiert ist, weisen wir dies mit dem Operator' << = 'zu. – PaulMcG

Verwandte Themen