Grails Goodness: Custom Data Binding with @DataBinding Annotation
Grails has a data binding mechanism that will convert request parameters to properties of an object of different types. We can customize the default data binding in different ways. One of them is using the @DataBinding
annotation. We use a closure as argument for the annotation in which we must return the converted value. We get two arguments, the first is the object the data binding is applied to and the second is the source with all original values of type SimpleMapDataBindingSource
. The source could for example be a map like structure or the parameters of a request object.
In the next example code we have a Product
class with a ProductId
class. We write a custom data binding to convert the String
value with the pattern {code}-{identifier}
to a ProductId
object:
package mrhaki.grails.binding
import grails.databinding.BindUsing
class Product {
// Use custom data binding with @BindUsing annotation.
@BindUsing({ product, source ->
// Source parameter contains the original values.
final String productId = source['productId']
// ID format is like {code}-{identifier},
// eg. TOYS-067e6162.
final productIdParts = productId.split('-')
// Closure must return the actual for
// the property.
new ProductId(
code: productIdParts[0],
identifier: productIdParts[1])
})
ProductId productId
String name
}
// Class for product identifier.
class ProductId {
String code
String identifier
}
The following specification shows the data binding in action:
package mrhaki.grails.binding
import grails.test.mixin.TestMixin
import grails.test.mixin.support.GrailsUnitTestMixin
import spock.lang.Specification
import grails.databinding.SimpleMapDataBindingSource
@TestMixin(GrailsUnitTestMixin)
class ProductSpec extends Specification {
def dataBinder
def setup() {
// Use Grails data binding
dataBinder = applicationContext.getBean('grailsWebDataBinder')
}
void "productId parameter should be converted to a valid ProductId object"() {
given:
final Product product = new Product()
and:
final SimpleMapDataBindingSource source =
[productId: 'OFFCSPC-103910ab24', name: 'Swingline Stapler']
when:
dataBinder.bind(product, source)
then:
with(product) {
name == 'Swingline Stapler'
with(productId) {
identifier == '103910ab24'
code == 'OFFCSPC'
}
}
}
}
If we would have a controller with the request parameters productId=OFFCSPC-103910ab24&name=Swingline%20Stapler
the data binding of Grails can create a Product
instance and set the properties with the correct values.
Written with Grails 2.5.0 and 3.0.1.