Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterating an object's own properties in Groovy?

Tags:

groovy

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.

like image 842
James Sumners Avatar asked Oct 21 '11 18:10

James Sumners


People also ask

How do you iterate the value of a object?

The Object. values() method outputs an array comprising the property values of the added object. After that, you can iterate through the object values by utilizing an array looping method such as JavaScript forEach() loop.

What language constructions do you use for iterating over object properties and array items?

What language constructions do you use for iterating over object properties and array items? — for loop, for..in, for each..in, map, reduce etc. This article on 2ality covers this subject in great detail. Explain the difference between mutable and immutable objects.

How do I iterate over an object in JavaScript?

There are two methods to iterate over an object which are discussed below: Method 1: Using for…in loop: The properties of the object can be iterated over using a for..in loop. This loop is used to iterate over all non-Symbol iterable properties of an object.


1 Answers

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"}
like image 179
tim_yates Avatar answered Dec 04 '22 06:12

tim_yates