Pyparsing: Parsen von Semi-JSON-geschachtelten Klartextdaten in eine Liste

Ich habe eine Reihe verschachtelter Daten in einem Format, das JSON stark ähnelt:

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=
{
    ...
}

Es gibt viele verschiedene Parameter mit unterschiedlichen Tiefenstufen - dies ist nur eine sehr kleine Teilmenge.

Es kann auch erwähnenswert sein, dass beim Erstellen eines neuen Sub-Arrays immer ein Gleichheitszeichen gefolgt von einem Zeilenumbruch gefolgt von einer offenen Klammer (wie oben gezeigt) angezeigt wird.

Gibt es eine einfache Schleifen- oder Rekursionstechnik zum Konvertieren dieser Daten in ein systemfreundliches Datenformat wie Arrays oder JSON? Ich möchte vermeiden, die Namen von Eigenschaften hart zu codieren. Ich suche nach etwas, das in Python, Java oder PHP funktioniert. Pseudocode ist auch in Ordnung.

Ich freue mich über jede Hilfe.

EDIT: Ich habe die Pyparsing-Bibliothek für Python entdeckt und es sieht so aus, als könnte sie eine große Hilfe sein. Ich kann keine Beispiele für die Verwendung von Pyparsing zum Analysieren verschachtelter Strukturen unbekannter Tiefe finden. Kann jemand Pyparsing anhand der oben beschriebenen Daten beleuchten?

EDIT 2: Okay, hier ist eine funktionierende Lösung in 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()

Das funktioniert, aber wenn ich versuche, die Ergebnisse mit json.dump (result) in eine Datei zu schreiben, wird der Inhalt der Datei in doppelte Anführungszeichen gesetzt. Es gibt auch\n Zeichen zwischen vielen der Datenpaare. Ich habe versucht, sie im obigen Code mit zu unterdrückenLineEnd().suppress(), aber ich muss es nicht richtig verwenden.

Okay, ich habe eine endgültige Lösung gefunden, die diese Daten tatsächlich in ein JSON-freundliches Diktat umwandelt, wie ich es ursprünglich wollte. Zunächst werden die Daten mithilfe von Pyparsing in eine Reihe verschachtelter Listen konvertiert, anschließend durch die Liste geschleift und in JSON umgewandelt. Dies ermöglicht es mir, das Problem zu überwinden, bei dem Pyparsing auftritttoDict() Methode konnte nicht verarbeiten, wenn dasselbe Objekt zwei Eigenschaften mit demselben Namen hat. Um festzustellen, ob es sich bei einer Liste um eine einfache Liste oder um ein Eigenschafts- / Wertepaar handelt, müssen Sie dieprependPropertyToken Methode fügt die Zeichenfolge hinzu__property__ vor Eigenschaftsnamen, wenn Pyparsing sie erkennt.

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]

Antworten auf die Frage(5)

Ihre Antwort auf die Frage