2 Stimmen

Externer Inhalt mit Groovy BuilderSupport

Ich habe einen benutzerdefinierten Builder in Groovy erstellt, indem ich BuilderSupport erweitert habe. Es funktioniert gut, wenn wie fast jeder Builder-Code-Beispiel da draußen konfiguriert:

def builder = new MyBuilder()
builder.foo {
    "Some Entry" (property1:value1, property2: value2)
}

Das funktioniert natürlich perfekt. Das Problem ist, dass ich nicht möchte, dass die Informationen, die ich aufbaue, im Code stehen. Ich möchte diese Informationen irgendwo in einer Datei haben, die vom Builder eingelesen und in Objekte eingebaut wird. Ich kann nicht herausfinden, wie man das macht.

Ich kann das nicht einmal durch Verschieben des einfachen Eintrags im Code zum Laufen bringen. Dies funktioniert:

def textClosure = { "Some Entry" (property1:value1, property2: value2) }
builder.foo(textClosure)

weil textClosure ein Abschluss ist.

Wenn ich das tue:

def text = '"Some Entry" (property1:value1, property2: value2)'
def textClosure = { text }
builder.foo(textClosure)

der Builder wird nur für den Knoten "foo" aufgerufen. Ich habe viele Varianten ausprobiert, einschließlich der Übergabe des Textblocks direkt in den Builder, ohne ihn in eine Schließung zu verpacken. Sie alle führen zum gleichen Ergebnis.

Gibt es eine Möglichkeit, ich nehme ein Stück beliebigen Text und übergeben Sie es in meinem Builder, so dass es in der Lage sein, richtig zu analysieren und bauen es?

1voto

noah Punkte 20711

Ihr Problem ist, dass ein String kein Groovy-Code ist. Die Art und Weise ConfigSlurper Dies geschieht durch Kompilieren des Textes in eine Instanz von Script 使って GroovyClassLoader#parseClass . z.B.,

// create a Binding subclass that delegates to the builder
class MyBinding extends Binding {
    def builder
    Object getVariable(String name) {
        return { Object... args ->  builder.invokeMethod(name,args) }
    }   
}

// parse the script and run it against the builder
new File("foo.groovy").withInputStream { input ->
    Script s = new GroovyClassLoader().parseClass(input).newInstance()
    s.binding = new MyBinding(builder:builder)
    s.run()
}

Die Unterklasse von Verbindlich gibt einfach einen Abschluss für alle Variablen zurück, der den Aufruf an den Builder delegiert. Angenommen also, foo.groovy enthält:

foo {
    "Some Entry" (property1:value1, property2: value2)
}

Es wäre gleichbedeutend mit Ihrem obigen Code.

0voto

Alexander Egger Punkte 4872

Ich denke, das von Ihnen beschriebene Problem lässt sich besser mit einem Slurper oder Parser lösen.

Siehe:

http://groovy.codehaus.org/Reading+XML+unter Verwendung von+Groovy%27s+XmlSlurper http://groovy.codehaus.org/Reading+XML+unter Verwendung von+Groovy%27s+XmlParser

für XML-basierte Beispiele.

In Ihrem Fall. Gegeben die XML-Datei:

<foo>
    <entry name='Some Entry' property1="value1" property2="value2"/>
</foo>

Du könntest es mit schlürfen:

def text = new File("test.xml").text
def foo = new XmlSlurper().parseText(text)
def allEntries = foo.entry
allEntries.each { 
    println it.@name
    println it.@property1
    println it.@property2
}

0 Stimmen

Diese Beispiele gehen immer noch davon aus, dass sich der zu parsende Inhalt im Klassenpfad befindet, was bei meiner Anwendung nicht der Fall sein wird. Ich möchte nicht XML für meine Konfiguration verwenden - ich habe mir den Quellcode sowohl für den XmlSlurper als auch für den XmlParser angesehen und beide sind sehr XML-spezifisch. Ich müsste meinen Slurper/Parser von Grund auf neu schreiben, weil es keine gemeinsame Basis gibt, wie es sie für BuilderSupport gibt.

0 Stimmen

Anscheinend habe ich dein Problem nicht verstanden. Ich habe ein Beispiel hinzugefügt, um zu verdeutlichen, was ich meinte. Vielleicht können Sie aufzeigen, wo das Problem bei dieser Lösung liegt.

0voto

Chad Punkte 464

Ursprünglich wollte ich die Möglichkeit haben, Folgendes anzugeben

"Some Entry" (property1:value1, property2: value2)

in einer externen Datei. Ich versuche insbesondere, XML und XML-ähnliche Syntax zu vermeiden, damit diese Dateien für normale Benutzer leichter zu erstellen und zu ändern sind. Meine derzeitige Lösung verwendet ConfigSlurper und die Datei sieht jetzt so aus:

"Some Entry"
{ 
   property1 = value1 
   property2 = value2 
}

ConfigSlurper erhalte ich eine Karte wie diese:

["Some Entry":[property1:value1,property2:value2]]

Es ist ziemlich einfach, diese Werte zu verwenden, um meine Objekte zu erstellen, vor allem, da ich die Eigenschaft/Wert-Map einfach an den Konstruktor übergeben kann.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X