Cléssio Mendes
Cléssio Mendes

Reputation: 1016

Updating groovy object fields from a map

In groovy we can easily create objects from maps and fill the corresponding fields automatically:

def myAddress = new Address([street:"King's street", number:"1200"])

Is it possible to also update an existing object from a map without recreating it? Something like...

myAddress.update([zip: "30555050", city: "London"])

Upvotes: 15

Views: 9410

Answers (3)

Bill K
Bill K

Reputation: 62789

After looking up/learning from Szymon's Excellent answer and finding a different way to invoke the helper, it seems like the answer can be simplified to:

InvokerHelper.setProperties(myAddress, [zip: "30555050", city: "London"])"

which is amazingly close to your requested

myAddress.update([zip: "30555050", city: "London"])

I added this as a comment to his question but it's so easy I thought it deserved a terse top-level answer of it's own.

Upvotes: 7

Raniz
Raniz

Reputation: 11123

You can use object."${variable}" accessors to do this:

map.each { key, value ->
    object."${key}" = value
}

You can then create a method that does this and install that on Object.metaClass and it will be available everywhere:

@Canonical
class MapSet {

    String name
    int count

    static def setAttributesFromMap(Object o, Map<String, Object> map) {
        map.each { key, value ->
            o."${key}" = value
        }
    }

    static void main(String[] args) {
        Object.metaClass.update = {
            setAttributesFromMap delegate, it
        }

        def o = new MapSet([
                name: "foo",
                count: 5
        ])

        assert o.name == "foo"
        assert o.count == 5

        o.update([
                name: "bar",
                count: 6
        ])

        assert o.name == "bar"
        assert o.count == 6
    }
}

Upvotes: 17

Szymon Stepniak
Szymon Stepniak

Reputation: 42262

You can use InvokeHelper category and setProperties method, here is a short example:

import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import org.codehaus.groovy.runtime.InvokerHelper

@EqualsAndHashCode
@ToString
class Address {
    String street
    String number
    String city
}

Address mainAddress = new Address(street: 'Test', number: '2B', city: 'London')

use InvokerHelper, {
    mainAddress.setProperties([street: 'Lorem', number: 'Ipsum'])
}

assert mainAddress.street == 'Lorem'
assert mainAddress.number == 'Ipsum'
assert mainAddress.city == 'London'

Although if you can avoid mutable objects, it's better for you. Otherwise you have to think about thread-safety to do not run into concurrency problems. You can use previous example to create a static method that expects 2 arguments: the existing object and a map of properties to update. In result you get a new instance that contains updated fields. Also you can make your class an immutable one.

Upvotes: 6

Related Questions