Iterating an object's own properties in Groovy?

11,063

There's this possibility (assuming I've got the right end of the stick) ;-)

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)
    }
}

If you don't have Groovy 1.7.9+, then the lines

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

Should be replaced with:

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

And I believe it will behave the same; ie: if I do this:

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

it prints out:

{"bar":"baz","myNumber":42,"tim":"yates"}
Share:
11,063
devdavid
Author by

devdavid

I am the Linux administrator at Clayton State University in Morrow, GA. I deal primarily with RHEL server administration, but I do a fair amount of scripting and web development as well. But programming is more of a hobby for me than day job. My current interest is in Node.js.

Updated on June 08, 2022

Comments

  • devdavid
    devdavid almost 2 years

    I have created a class that allows the resulting object to have arbitrary properties added to it. The class also has some predefined properties. In a method of the class I want to be able to iterate over all properties owned by the instance of the object.

    Here is an example class definition:

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

    Is there a way to do what I am wanting to do?

    Edit: One of my goals is to not have to explicitly name my predefined properties in the toJsonString method. I want to be able to add new predefined properties at a later date without having to remember to update the toJsonString method.

    Edit (24 October 2011):

    The accepted answer gave me the information I needed. However, it still required me to name the properties I don't want included in the JSON string. Extending the answer a little bit solves this problem:

    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] ]
    }
    

    For this to work, you must explicitly specify the modifiers for your class properties. That is String bar = "baz" in my example should be public String bar = "baz" in order for it to be included in the JSON string.