Embracing the Messiness in Search of Epic Solutions

Groovy: Copying Properties Between Two Beans

Posted

in

PROBLEM

Given two beans…

class A {
    String name
    LocalDateTime localDateTime
}

class B {
    String name
    LocalDateTime localDateTime
}

There are several ways to copy properties from one bean to another:-

  • The most rudimentary way is to “get” each property from one bean and “set” it on another bean, which is VERY verbose and stupid.
  • Another way is to leverage utilities such as BeanUtils provided by either Apache Commons or Spring. While both libraries are called BeanUtils, they behave slightly different from one another.
  • Write home-grown reflection function… and now you have two problems: 1) it may not handle edge cases properly and 2) no one understands your implementation.

SOLUTION

Groovy provides a helper class to solve this problem called InvokerHelper. The advantage of using this is there’s no need to import yet another dependency and it still allows us to keep our code concise.

Scenario 1: Both beans have exact properties

class MySpec extends Specification {
    class A {
        String name
        LocalDateTime localDateTime
    }

    class B {
        String name
        LocalDateTime localDateTime
    }

    def "given a and b with same exact properties, should copy all properties"() {
        given:
        def a = new A(name: 'name',
                      localDateTime: LocalDateTime.now())
        def b = new B()

        when:
        InvokerHelper.setProperties(b, a.properties)

        then:
        b.name == a.name
        b.localDateTime == a.localDateTime
    }
}

Scenario 2: Source bean has additional properties

class MySpec extends Specification {
    class A {
        String name
        LocalDateTime localDateTime
        Integer extra1
        Boolean extra2
    }

    class B {
        String name
        LocalDateTime localDateTime
    }

    def "given a has additional properties than b, should ignore additional properties"() {
        given:
        def a = new A(name: 'name',
                      localDateTime: LocalDateTime.now(),
                      extra1: 1,
                      extra2: true)
        def b = new B()

        when:
        InvokerHelper.setProperties(b, a.properties)

        then:
        b.name == a.name
        b.localDateTime == a.localDateTime
    }
}

Scenario 3: Destination bean has additional properties

class MySpec extends Specification {
    class A {
        String name
        LocalDateTime localDateTime
    }

    class B {
        String name
        LocalDateTime localDateTime
        Integer extra1
        Boolean extra2
    }

    def "given b has additional properties than a, should set additional properties as null"() {
        given:
        def a = new A(name: 'name',
                      localDateTime: LocalDateTime.now())
        def b = new B()

        when:
        InvokerHelper.setProperties(b, a.properties)

        then:
        b.name == a.name
        b.localDateTime == a.localDateTime
        b.extra1 == null
        b.extra2 == null
    }
}

Scenario 4: Same property but different data type from each bean

The short answer is don’t do it. It’s not worth the hassle and confusion.

class MySpec extends Specification {
    class A {
        String number
    }

    class B {
        Integer number
    }

    def "given same property name but different data type, should go bat shit crazy"() {
        given:
        def a = new A(number: '0')
        def b = new B()

        when:
        InvokerHelper.setProperties(b, a.properties)

        then:
        b.number == 48 // ASCII value for character '0'
    }

    def "given same property name but different data type, should go bat shit crazy again"() {
        given:
        def a = new A(number: '10')
        def b = new B()

        when:
        InvokerHelper.setProperties(b, a.properties)

        then:
        thrown ClassCastException // because there's no ASCII value for character '10'
    }
}

Tags:

Comments

One response to “Groovy: Copying Properties Between Two Beans”

  1. zeroblaz3 Avatar
    zeroblaz3

    Thank you. BeanUtils does not play nice with Groovy. InvokeHelper was the answer.

Leave a Reply to zeroblaz3Cancel reply