Copy Groovy class properties

Arturo Herrero picture Arturo Herrero · Jan 31, 2012 · Viewed 16.8k times · Source

I want to copy object properties to another object in a generic way (if a property exists on target object, I copy it from the source object).

My code works fine using ExpandoMetaClass, but I don't like the solution. Are there any other ways to do this?

class User {
    String name = 'Arturo'
    String city = 'Madrid'
    Integer age = 27
}

class AdminUser {
    String name
    String city
    Integer age
}

def copyProperties(source, target) {
    target.properties.each { key, value ->
        if (source.metaClass.hasProperty(source, key) && key != 'class' && key != 'metaClass') {
            target.setProperty(key, source.metaClass.getProperty(source, key))
        }
    }
}

def (user, adminUser) = [new User(), new AdminUser()]
assert adminUser.name == null
assert adminUser.city == null
assert adminUser.age == null

copyProperties(user, adminUser)
assert adminUser.name == 'Arturo'
assert adminUser.city == 'Madrid'
assert adminUser.age == 27

Answer

Michal Z m u d a picture Michal Z m u d a · May 10, 2014

I think the best and clear way is to use InvokerHelper.setProperties method

Example:

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

@ToString
class User {
    String name = 'Arturo'
    String city = 'Madrid'
    Integer age = 27
}

@ToString
class AdminUser {
    String name
    String city
    Integer age
}

def user = new User()
def adminUser = new AdminUser()

println "before: $user $adminUser"
InvokerHelper.setProperties(adminUser, user.properties)
println "after : $user $adminUser"

Output:

before: User(Arturo, Madrid, 27) AdminUser(null, null, null)
after : User(Arturo, Madrid, 27) AdminUser(Arturo, Madrid, 27)

Note: If you want more readability you can use category

use(InvokerHelper) {
    adminUser.setProperties(user.properties) 
}