I use grails 1.3.2 and hbase..
I have domain class, one of which fields is in enum type:
class MyDomainClass{
MyEnum enumVal
//....
}
public enum MyEnum {
val1("val1"),
val2("val2")
final String value
MyEnum (String value) {
this.value = value
}
String toString() { value }
String getKey() { name() }
}
<g:form action="create">
<g:select name="enumVal" from="${MyEnum ?.values()}" optionKey="key" />
<g:submitButton name="createOb" value="CreateOb"/>
</g:form>
"create" action have to save selected value in db.
When I submit I get exception:
Cannot cast object 'val1' with class 'java.lang.String' to class 'myPack.MyEnum '
Is there any way to save enum value as a String?
The space after "MyEnum" in GSP and error message makes me doubt, can you remove it from GSP?
You don't need ?, as MyEnum class should always be there.
I believe you don't need optionKey, especially if you have overridden MyEnum.toString().
We write selects from enum this way:
<g:select from="${SomeEnum.values()*.toFriendlyString()}"
keys="${SomeEnum.values()*.name()}"
value="${xxxInstance.field.name()}" ...
/>
where toFriendlyString() is our Enum's method that returns user-readable String representation.
It seems to be a data-type conversion issue. You may try:
def domainObject = new MyDomainClass()
def enumValue = myPack.MyEnum.valueOf(params.enumVal) // This is the conversion.
After that, assign your domain object with the new enumValue.
Related
Let's assume we have an enum, that represents searchable fields:
enum SearchableFields {
ALL,
FIELD1,
FIELD2,
FIELD3;
}
This enum is displayed via a (combobox) selection inside a GUI. At runtime, I want to evaluate the selection of this combobox and search accordingly.
Depending on the selection, I need to retrieve the fields to search from a POJO (example below) via a getter.
class FieldPojo {
private String field1;
private String field2;
private String field3;
...
public String getField1() {
return field1;
}
...
}
I currently use a switch statement to evaluate the selection of SelectableFields and to then retrieve the correct field(s) to search:
private String retrieveField(FieldPojo f) {
switch (selectedField) {
case ALL:
return retrieveAll(); // method that retrieves all available fields
case FIELD1:
return f.getField1();
...
}
This does work, however I feel like it's clunky.
Question:
Is there a more concise way to do this without evaluating the enum via a switch? (Java 8)
You could store a reference to the getter in your enum constants:
enum SearchableFields {
ALL(FieldPojo::retrieveAll),
FIELD1(FieldPojo::getField1)
private final Function<FieldPojo, String> accessor;
SearchableFields(Function<FieldPojo, String> acccessor) {
this.accessor = accessor;
}
public String get(FieldPojo fp) {
return accessor.apply(fp);
}
}
You can create a static map instead of the switch-case.
private static final Map<SearchableFields,Supplier<String>> searchableFieldsToFieldPojo = Map.of(
ALL, this::retrieveAll,
FIELD1, FieldPojo::retrieveAll
);
And then you can access via:
searchableFieldsToFieldPojo.get(selectedField).get();
Given that you can modify all parts of the code, you have several options:
Put the retrieveField into the class FieldPojo and modify it's parameter so it takes the enum SearchableFields as parameter.
Put the fields of FieldPojo as values into a map with a key of type SearchableFields. You can then decide whether you want to have "ALL" as an extra entry of the map or handle it as special case in a method similar to retrieveField. You could use this to have a "default" handling if you want to update the enum but not the FieldPojo class.
You put retrieveField into the class FieldPojo together with the SearchableFields enum - since only FieldPojo knows, which fields it actually provides as searchable fields.
You use introspection to gather the list of possible searchable fields and also access their contents.
Depending on your real requirements (you only showed a very abstract and specific version of them) one or the other method might be "the right one" for you. I would actually prefer the "everything into FieldPojo" as the most robust one, but on the other hand if you are not able to change FieldPojo and have to handle many different classes like it, the introspection variant might be the right one. (Be aware that it is fragile in terms of security and also probably very slow.)
Enums can contain method definitions, so one way is to define the method that retrieves the field name based on the enum value. I assume you have the actual field name stored as a member field also. Then you can override the method for the special ALL value:
enum SearchableFields {
ALL("all") { // all is just a placeholder in this case
#Override
String retrieveField(FieldPojo f) {
// logic for all fields
}
},
FIELD1("field1"),
FIELD2("field2"),
FIELD3("field3");
SearchableFields(String fieldName) {
this.fieldName = Optional.of(fieldName);
}
SearchableFields() {
fieldName = Optional.empty();
}
private final Optional<String> fieldName;
String retrieveField(FieldPojo f) {
if (fieldName.isPresent()) {
return (String) f.getClass().getField(fieldName.get()).get(f);
} else {
// ...
}
}
}
Here's my situation,
I have a class with Enum type fields. I want to execute annotated validation for enum types, similar to annotations for strings, example: #Size, #NotNull etc.
Problem is, json deserializer fails on enum type before validation occurs.
public class myClass {
#JsonProperty
//#SomeCustomValidator -- ??
private EnumType enumValue;
}
public enum EnumType {
A,
B,
C
}
Few things:
I do not want to change the data type to String.
Tried steps in following threads, didn't fix my problem.
Tried this link, but get an error in deserialization before validation hits
Tried this link, but it works only when data Type is String
Validation works after the type of the argument is resolved. So I don't see a way how to use String validating annotations on enums. As workaround you can use #JsonCreator and do some validation before object creation.
public enum EnumType {
A,
B,
C;
#JsonCreator
public static EnumType from(String s) {
// add your logic here, for example
/*
if (Stream.of(values()).map(Enum::name).noneMatch(name -> name.equals(s))) {
throw new MyServiceException("Bad value " + s);
}
*/
return valueOf(s);
}
}
I have an enum something like this
public Enum MyEnum {
NEW("NEW"),
OLD("OLD"),
IN_PROCESS("IN PROCESS");
}
The mapping on ibatis works fine for the NEW and OLD, but encounters an error when the IN_PROCESS is encountered since the value of my IN_PROCESS in the DB is IN PROCESS, and the error indicates that ibatis tries to find an enum with that value, can someone suggest a solution?
MyBatis use an EnumTypeHandler to do mappings with enums. In this Enum type handler it uses the name() method of the Enums which returns the string value of the variable name.
For example NEW -> "NEW" and IN_PROCESS -> "IN_PROCESS".
Otherwise, to get the value it uses Enum.valueOf(type, s); which gets the value of the Enum through the String value which corresponds with the variable name ("NEW" -> MyEnum.NEW, "IN_PROCESS" -> MyEnum.IN_PROCESS) and internally is used the method name().
You cannot overwrite name() because is marked as final so you options are:
The easy way is to use IN_PROCESS instead of IN PROCESS. I prefer this, is easier and fast.
The second option is create your TypeHanlder for this enum and check if the parameter is IN PROCESS and search with IN_PROCESS.
I had the same issue, I ended up writing a custom setter on my POJO to convert the string value.
public Enum MyEnum {
NEW("NEW"),
OLD("OLD"),
IN_PROCESS("IN PROCESS");
public static MyEnum fromValue(String v){
.... find the enum based on value
}
}
public class POJO {
private MyEnum myEnum;
public void setMyEnum(String strV){
myEnum=MyEnum.fromValue(strV)
}
}
What I am trying to do is the following: Given a JSON document, map it to a POJO using Jackson, but define the type of the Generic class member based on a field in the JSON document.
My JSON looks as follows
{
"name": "Name",
"parameters": [
{"name": "paramName","value": "Value1", "#type": "string"},
{"name": "size","value": 5,"#type": "double"}
]
}
The class that maps to this JSON doc is
public class Strategy {
public String name;
public List<Parameter<?>> parameters;
}
Then I have a Generic class for this as follows
public class Parameter<T> {
public String name;
public T value;
#Override
public String toString() {
return this.getClass().getName();
}
}
So the idea is to tell Jackson when you deserialize the JSON document into the Strategy class and get to the parameters field, use the following classes as the Generic data type for the value member, i.e. I want to select it to be String or Double or Integer but I want that to be my decision so that it's generic and can be extended to any data type I want.
I realise I can use the annotation JsonTypeInfo which I added as well like this
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#type")
But using these classes as is actually works but Jackson decides itself what the type should be based on its value and my size parameter is set to an Integer. If I set its value to 5.0 then its set to a Double which works, but what if I want one of the parameters to be a custom object?
The only way I could get this to work (and am not 100% happy with the solution) is to make the Parameter class abstract and then create concrete classes for each type that I want, i.e. ParameterString, ParameterDouble, ParameterCustomClass and then use the #JsonSubTypes annotations to set the correct class to use based on the type field in the JSON document.
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#type")
#JsonSubTypes({
#JsonSubTypes.Type(value=ParameterString.class, name="string"),
#JsonSubTypes.Type(value=ParameterDouble.class, name="double"),
#JsonSubTypes.Type(value=ParameterInstrument.class, name="instrument")
})
With the following class as an example
public class StrategyParameterString extends StrategyParameter<String> {
}
This isn't very extendable, I guess it will just need a new subtype annotation and concrete class added for every type that I need, but just doesn't feel as elegant as it could be.
Does anyone know of a better way of handling this ?
Thanks
Andrew
As I understand it, the types you want to represent in your Parameter list are reifiable, eg. String, Double, Instrument. You can take advantage of the fact that reifiable types have a runtime type token in the form of their class literal. This can be exploited to form the basis of a heterogenous type safe collection.
Instead of defining your Parameter class this way:
public class Parameter<T> {
public String name;
public T value;
:
:
}
}
You can define it as a concrete class that associates the object's value with its run time type token.
public class Parameter() {
private final Object m_value;
private final Class<?> m_cls;
private Parameter(Class<?> token, Object val) {
m_value = val;
m_cls = token;
}
public static <T> Parameter newInstance(Class<T> token, T value) {
return new Parameter(token, value);
}
:
:
public <T> T getValue(Class<T> token) {
if (token != m_cls) throw new ClassCastException("Type error");
return token.cast(m_value);
}
}
In this setting, type tokens and generic methods (rather than a generic type) are used to set and reestablish the type linkage for the desired value. The value you set can be any type and is guaranteed to be returned as the same type that you stored as long as the type tokens are consistent.
Note that constructors can not be generic. To address this, the constructor for Parameter has been made private and Parameter instances are formed by invoking the newInstance() static factory method (which can be generic).
I have an enum FooBar at class Clazz with falues FOO and BAR like this:
class Clazz {
enum FooBar{
FOO,
BAR
}
}
I now would like to use wicket getString() method to localize the values FOO and BAR. The best I can do is to define at i18n file
Clazz.FooBar.FOO=foo
Clazz.FooBar.BAR=bar
and I get values with this code
fooBar = FooBar.FOO;
getString("Clazz.FooBar." + fooBar.name());
I have heard that this could be achieved without Clazz.FooBar addition to the i18n query string, but the method to be called would be different. How to do this?
You can put this method in your base page/panel:
public String getString(Enum<?> value) {
Class<?> enclosingClass = value.getClass().getEnclosingClass();
String key = (enclosingClass == null ? "" : enclosingClass.getSimpleName() + ".")
+ value.getClass().getSimpleName() + "." + value.name();
return getString(key);
}
Then you can simply call it with
getString(Clazz.FooBar.FOO);
and it will return what you defined in the property file.
I will not advice you to directly store enum constant names in properties file the reason is simple two different enums can hold same name.
Below is the code I have come up with
class Clazz {
enum FooBar {
//StrId are keys from property file e.g. below
FOO("com.abc.classz.foobar.FOO"), BAR("com.abc.classz.foobar.BAR");
private final String strId;
private FooBar(String id) {
this.strId = id;
}
// toString can also be used here I am just keen on having seperate
// method
public String getName() {
//Load Value for strId from properties file
return null;
}
}
}
This will keep your enum and your i18n purpose separate and clear.
See below sample Enum class. You may want to customize it more depending on your needs.
public enum FooBar {
foo("foobar.foo"),
bar("foobar.bar");
private String key;
ErrorCodeEnum(final String key) {
this.key = key;
}
public String toString() {
return key;
}
}
then you can make the toString method to return key directly so you can use
getString(ErrorCodeEnum.ERROR1);
or you can override the toString method directly like below
public enum FooBar {
foo, bar;
public String toString(){
return getClass().getName()+"."+name();
}
}
You could simply define
FOO=foo
BAR=bar
in your properties and access it by
getString(fooBar.name());
or am I missing some point?
I was looking for something called EnumChoiceRenderer. The main idea is to give a EnumChoiceRenderer for e.g. DropDownChoise element and you're able to give parameters of the kind I was proposing in my question. Ok, in this solution you're able to give only
FooBar.BAR=bar
FooBar.FOO=foo
in your resource file but this is the closest I could find when I investigated this more with my spare time.
PS. Click the EnumChoiseRenderer in the beginning of this answer to see the article of this solution.