0
votes

I am trying to upgrade my grails application from version 1.3 to 2.5. I have a domain class with one of its fields as Enum. In controller, when I do domainObj.validate(), it always returns false. Validation error is -

grails.validation.ValidationErrors: 1 errors Field error in object 'Parameter' on field 'typeEnum': rejected value [0]; codes [com.TypeEnum.typeMismatch.error,com.TypeEnum.typeMismatch,com.TypeEnum.typeMismatch.error,com.TypeEnum.typeMismatch,typeMismatch.com.TypeEnum,typeMismatch.pspValueTypeEnum,typeMismatch.com.TypeEnum,typeMismatch]; arguments [typeEnum]; default message [No enum constant com.TypeEnum.0]

EDIT: On further analysis, I found that the issue is not with enums, but when parameterObj.properties = params is executed, parameterObj.validate() returns false.

In my code with Grails 2.5,

parameterObj.validate()   //returns true

parameterObj.properties = params
// code to convert String from params to Enum object and assign it to parameterObj
parameterObj.enumField = MyEnumClass.getEnumByName(params.enumNameValue)
parameterObj.validate()   //This always return false

Note: In both above cases of validate(), field values of parameterObj are exactly same

Are there any changes in using properties on domain class in Grails 2.5 ?

2
can you please provide the domain class property definition of the failing fields, as well as a printout of the keys/values in params? if the error still is the posted ValidationError, then grails apparently tries to write the string "0" to the TypeEnum field, which of course fails, because the string value needs to be converted into a TypeEnum - norganos
Yes, Grails wont accept "0" to the TypeEnum. So before validating I am converting "0" to enum object and assigning it to domain object. Edited the code above to be more specific - nshweta

2 Answers

0
votes

if you change your views, so that they send the name() of the enums as a parameter value, then data binding works out of the box.

at least that's how I use enums all the time.

Enum Data Binding with name()

<g:select name="typeEnum" from="${TypeEnum.values()}" value="${obj.typeEnum}"/>

grails then uses the .name() of each enum value as value and the .toString() as displayed text. this will generate something like this:

<select name="typeEnum">
  <option value="STRING">String</option>
  <option value="NUMBER">Number</option>
  ...
</select>

data binding in controllers then works fine with domain or command objects:

def save1(PsparameterCmd cmd) {
  ...
  instance.properties = cmd.properties
}
def save2() {
  ...
  instance.properties = params
}

Custom Enum Data Binding

alternatively, you can use custom data binding to translate the IDs to the enum value.

there are different approaches (documentation: http://docs.grails.org/2.5.0/guide/theWebLayer.html#dataBinding): * you can register a global data binder for that type (if you use that enum a lot in your app) * you can specify a dataBinding annotation on that single field (for single usage)

class Psparameter implements java.io.Serializable {
  @BindUsing({ obj, params ->
    return TypeEnum.values().find({ it.id == params.int('typeId') })
  })
  TypeEnum typeEnum
  ...
}

or the global converter:

import org.grails.databinding.converters.ValueConverter
class TypeEnumConverter implements ValueConverter {
  boolean canConvert(value) {
    value instanceof Integer
  }
  def convert(value) {
    return TypeEnum.values().find({ it.id == value })
  }
  Class<?> getTargetType() {
    TypeEnum
  }
}

which you have to register in resources.groovy

beans = {
  typeEnumConverter(your.package.TypeEnumConverter)
}

Note: As far as I know (and just tested), there is no generic method like TypeEnum.getTypeEnumById(value). That's why I use a find on TypeEnum.values() in the examples above.

0
votes

After struggling for quite some time, I was able to find a solution for this behaviour in Grails 2.5. Here is my solution and observations. Pasting my problem code for again for reference:

parameterObj.validate()   //returns true

 parameterObj.properties = params
// code to convert String from params to Enum object and assign it to parameterObj
parameterObj.enumField = MyEnumClass.getEnumByName(params.enumNameValue)
parameterObj.validate()   //This always return false
  1. If a field (like enumField in my example) is an enum then enum string value is expected to be passed to domain object (parameterObj) and not the enum object. For example, if my enum is

     enum TypeEnum {
        STRING(0, "String"), NUMBER(1, "Number"), TIME(2, "Time")
         ...
       }
    

then value to be sent to domainObj is "STRING" or "NUMBER", etc

  1. In my case if I update the enumField with "STRING" after assigning params to properties then validate still returns false. For example,

     parameterObj.properties = params
     parameterObj.enumField = "STRING"
     parameterObj.validate()  // returns FALSE
    

    So I had to update enumField in params before doing parameterObj.properties = params to get correct result by validate() method. So the below code in this sequence works fine.

     params.enumField = "STRING"
     parameterObj.properties = params
     parameterObj.validate()  // returns True as expected
    

    This was not the case with earlier versions of Grails, my application worked fine for years on Grails version 1.3.3