2011-02-10 10 views
4

Grails JSONBuilderの最新バージョンを使用して同じオブジェクトを2回以上発光させたときにクロージャを繰り返さないようにする方法を知っている人はいますか?JSONBuilderを使用してカットアンドペーストのクロージャを削除する方法

MembersLeadersのセットを含むGroupドメインオブジェクトがあります。閉鎖を切り取って貼り付けることなく人を放出する方法を見つけたいと思います。

def builder = new JSONBuilder() 
    def result = builder.build { 
     array { 
      Group.list().each { Group group -> 
       g = { 
        id = group.id 
        name = group.name 

        members = array { 
         group.members?.person?.sort().each { Person person -> 
          m = { // Person closure copy #1 
           id = person.id 
           firstName = person.firstName 
           lastName = person.lastName 
          } 
         } 
        } 

        leaders = array { 
         group.leaders?.person?.sort().each { Person person -> 
          l = { // Person closure copy #2 
           id = person.id 
           firstName = person.firstName 
           lastName = person.lastName 
          } 
         } 
        } 
       } 
      } 
     } 
    } 

私は別に閉鎖を定義しようとしたが、それは、次のようなエラーにつながる:exception: No such property: id for class:


いくつかの注意事項:

1)の例では、ドメイン・オブジェクトが大幅に簡略化されます。 render Group.list() as JSONまたはrender Group.list().encodeAsJSONの代わりにJSONBuilderを使用しています。これは、オブジェクトのどの部分がエンコードされているかを制御する必要があるためです。

2)これができない理由を説明する正式な回答を受け入れることにします。

+0

「クロージャを個別に定義するのはどうですか?」 私はデフクロージャ= {...}があなたのケースでうまくいくはずだと信じています... – jpertino

+0

それは私が試みたものです。私はそれがうまくいくと思っていたが、そうはしなかった。 – lambmj

答えて

3

、私は解決策を持っています。クロージャを直接使用するのではなく、マップを返すクロージャを使用します。

class Person { 
    ... 

    def toMap = { 
     def map = [:] 
     map["id"] = this.id 
     map["firstName"] = this.firstName 
     map["lastName"] = this.lastName 
     return map 
    } 
} 

def builder = new JSONBuilder() 
def result = builder.build { 
    array { 
     Group.list().each { Group group -> 
      g = { 
       id = group.id 
       name = group.name 

       members = array { 
        group.members?.person?.sort().each { Person person -> 
         m(person.toMap()) 
        } 
       } 

       leaders = array { 
        group.leaders?.person?.sort().each { Person person -> 
         l(person.toMap()) 
        } 
       } 
      } 
     } 
    } 
} 

m(person.toMap())構文は直感的ではありませんが、それは動作し、私は自分自身の繰り返しを避けることができます。 This blog entryは、現在のGrails JSONBuilderの詳細を提供し、その起源について説明します。

1

同じ「コンテキスト」で別のクロージャを実行するには、クロージャの「デリゲート」を設定します。 クロージャは、マルチスレッドセーフではありません(一度に1つのデリゲートのみ)。クロージャがシングルトンクラスまたは静的変数で共有される場合、クロージャを毎回クローンする必要があります。

これはコードをリファクタリングする単なるアイデアですが、動作しない可能性があります(私はテストしませんでした)。 DSLのプロパティ名またはメソッド名を動的に決定する必要がある場合は、代入(=)を「setProperty」に、DSLメソッド呼び出しを「invokeMethod」に置き換えることができます。クロージャを用いて繰り返し失敗後 (http://groovy.codehaus.org/api/groovy/lang/Closure.htmlを参照)

def personClosure = { Person person, varname -> 
    setProperty(varname, { 
     id = person.id 
     firstName = person.firstName 
     lastName = person.lastName 
    }) 
} 

def groupMembersClosure = { memberList, memberListVarName, memberVarName -> 
    personClosure.delegate = delegate 
    setProperty(memberListVarName, array { 
     memberList?.person?.sort().each personClosure, memberVarName 
    }) 
} 

def builder = new JSONBuilder() 
def result = builder.build { 
    array { 
     Group.list().each { Group group -> 
      g = { 
       id = group.id 
       name = group.name 

       groupMembersClosure.delegate = delegate 

       groupMembersClosure(group.members, 'members', 'm') 
       groupMembersClosure(group.leaders, 'leaders', 'l') 
      } 
     } 
    } 
} 
+0

コントローラでこれを使用しようとすると、 'Error 500:コントローラ[com.test.people.PeopleController]のアクション[getGroups]を実行すると例外が発生しました:クラス:comのm:そのようなプロパティはありません。 test.people.PeopleController'だから私は間違ったことをしているに違いない。私は 'delegate'を' builder.delegate'に設定しようとしました。 – lambmj

+0

私は 'groupMembersClosure.resolveStrategy = Closure.DELEGATE_FIRST'を設定しようとしましたが、それでも喜びはありません。 – lambmj

関連する問題