【问题标题】:Iterating an object's own properties in Groovy?在 Groovy 中迭代对象自己的属性?
【发布时间】:2011-10-21 18:39:29
【问题描述】:

我创建了一个类,它允许生成的对象添加任意属性。该类还具有一些预定义的属性。在类的方法中,我希望能够遍历对象实例拥有的所有属性。

这是一个示例类定义:

import groovy.json.*

class Foo {
    private Map props = [:]

    String bar = "baz"
    def myNumber = 42

    void propertyMissing(String name, Object value) {
        this.props[name] = value
    }

    def propertyMissing(String name) {
        return this.props[name]
    }

    def toJsonString() {
        def outObject = [:]

        // I want to do something like this
        this.properties.each { k, v ->
            if (this.isOwnProperty(k) && k != 'props') {
                outObject[k] = v
            }
        }

        outObject = outObject + this.props

        return JsonOutput.toJson(outObject)
        // Should return a string like:
        // {"bar":"baz", "myNumber":42, "someDynamicProperty":"value"}
        //
        // This string should not contain the "class" and "metaClass"
        // properties.
    }
}

有没有办法做我想做的事?

编辑: 我的目标之一是不必在 toJsonString 方法中明确命名我的预定义属性。我希望能够在以后添加新的预定义属性,而不必记得更新 toJsonString 方法。

编辑(2011 年 10 月 24 日):

接受的答案为我提供了所需的信息。但是,它仍然需要我命名我不想包含在 JSON 字符串中的属性。稍微扩展一下答案就解决了这个问题:

def outObject = Foo.declaredFields.findAll {
    // 'it' is a Field object returned by
    // http://download.oracle.com/javase/1,5,0/docs/api/java/lang/Class.html#getDeclaredFields()
    !it.synthetic &&
    it.getModifiers() != java.lang.reflect.Modifier.PRIVATE
}.collectEntries { v ->
    [ (v.name) : this[v.name] ]
}

为此,您必须明确指定类属性的修饰符。在我的示例中,String bar = "baz" 应为 public String bar = "baz",以便将其包含在 JSON 字符串中。

【问题讨论】:

    标签: groovy


    【解决方案1】:

    有这种可能性(假设我掌握了正确的方法);-)

    class Foo {
        private Map props = [:]
    
        String bar = "baz"
        def myNumber = 42
    
        void propertyMissing(String name, Object value) {
            this.props[name] = value
        }
    
        def propertyMissing(String name) {
            return this.props[name]
        }
    
        def toJsonString() {
            def outObject = Foo.declaredFields.findAll { !it.synthetic && it.name != 'props' }.collectEntries { v ->
              [ (v.name):this[v.name] ]
            }
            outObject << props
            JsonOutput.toJson(outObject)
        }
    }
    

    如果你没有 Groovy 1.7.9+,那么这些行

            def outObject = Foo.declaredFields.findAll { !it.synthetic && it.name != 'props' }.collectEntries { v ->
              [ (v.name):this[v.name] ]
            }
    

    应替换为:

            def outObject = Foo.declaredFields.findAll { !it.synthetic && it.name != 'props' }.inject([:]) { m, v ->
              m << [ (v.name):this[v.name] ]
            }
    

    而且我相信它的行为会相同;即:如果我这样做:

    def f = new Foo()
    f.tim = 'yates'
    println f.toJsonString()
    

    打印出来:

    {"bar":"baz","myNumber":42,"tim":"yates"}
    

    【讨论】:

    • 谢谢。这正是我想要做的。
    【解决方案2】:

    如果您想坚持使用此实现,也许覆盖 getProperties 以包含您的属性映射将是最简单的。

    【讨论】:

    • 如果我正确理解您的建议,标准的fooInstance.properties() 方法已经包含(不是真的)私人地图。我更新了我的问题,提供了有关我的目标的更多信息。
    • @jsumners 它包含地图,但作为地图,而不是作为“顶级”属性的条目。我的意思是创建一个不包含 map-as-map 的 getProperties,而是将映射的键作为属性名称,将值作为这些属性的值。
    • 好的。那讲得通。但是,当我 def getProperties(){ return this.props.keySet() } 尝试设置动态属性时,会导致“无此类属性”错误。此外,我仍然想排除继承的属性,如“类”和“元类”,而不必提前知道它们。这个答案没有解决问题的那一部分。
    • 你不能just返回 this.props.keySet(),因为那样你会错过实际的属性;这就是为什么我说要 include 属性映射的条目。元类属性列表很短且已知,或者您可以通过过滤 super.properties 显式删除超/元类属性。但是您是否考虑过只使用 ExpandoMetaClass?这就是为什么我以“坚持这个实现”作为开头的原因,因为 Groovy 已经有一种默认方式来表示一个在运行时才知道其属性的类。
    • 我现在看到我在尝试返回 keySet 时误读了文档;这是漫长的一天。但是,我仍然有这个问题,因为现在我似乎无法创建 groovy.lang.MetaProperty 的实例。解释器要么在我的“Foo”对象上查找 MetaProperty 方法,要么在“groovy”对象(即groovy.lang.MetaProperty)上找不到“lang”。让我明确一点:我不知道 Groovy。一个实际的例子将使我受益匪浅。
    猜你喜欢
    • 1970-01-01
    • 2023-03-24
    • 2012-11-21
    • 2010-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-26
    • 2010-10-03
    相关资源
    最近更新 更多