如何在使用JSONBuilder时消除剪切和粘贴闭包

时间:2011-02-10 13:40:00

标签: grails groovy

有没有人知道使用最新版本的Grails JSONBuilder多次发出同一个对象时避免重复闭包的方法?

我有一个Group域对象,其中包含MembersLeaders的集合。我想找到一种方法来排出一个人而不必剪掉和粘贴封口。

    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)示例中的域对象大大简化。我正在使用JSONBuilder而不是render Group.list() as JSONrender Group.list().encodeAsJSON,因为我需要控制对象的哪些部分被编码。

2)我会接受权威的答案,解释为什么不能这样做。

2 个答案:

答案 0 :(得分: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”替换赋值(=),使用“invokeMethod”替换DSL方法调用。 (参考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')
            }
        }
    }
}