Cannot cast enum to String while persisting Object in hibernate - java

I am trying to persist my menuItem Object in the database. One of the itemFields is an ItemType string which is bounded by ItemType enum. Here is my MenuItem POJO.
public class MenuItemImpl implements MenuItem{
private long id;
private String itemName;
private String itemType;
private int itemPrice;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName= itemName;
}
public ItemType getItemType() {
return ItemType.valueOf(itemType);
}
public void setItemType(ItemType itemType) {
this.itemType=itemType.toString();
}
public int getItemPrice() {
return this.itemPrice;
}
public void setItemPrice(int itemPrice) {
this.itemPrice=itemPrice;
}
}
This is my ItemType enum
public enum ItemType {
BURGER("BURGER"), BEVERAGE("BEVERAGE"), SNACK("SNACK"), TOY("TOY");
private String value;
private ItemType(String value) {
this.value = value;
}
public String getValue() {
return value;
}
#Override
public String toString() {
return value;
}
}
Now when I try to do a save on this object. I get the following exception:
com.saggezza.ItemType cannot be cast to java.lang.String
I dont see why it should even try casting ItemType. as my getter and setter methods in the menuItem POJO already take care of the conversion of enum to string and string to enum.
Any ideas?

The problem is that Hibernate is using your getters/setters to access your property, so when it wants to get the value of the itemType field to save it, it will call getItemType(), expecting it to return a String (what is in the mapping), and will get an ItemType instead, thus the exception.
You can have a look at this answer to see how you can add your enum to your mapping directly.

I notice in this method:
public void setItemType(ItemType itemType) {
this.itemType=itemType.toString();
}
You have the parameters specified as itemType and the global String variable itemType with the same name, to make it easier to support your code (and figure out where the error is occurring) I would suggest renaming the parameter to item. So like this:
public void setItemType(ItemType item) {
this.itemType=item.toString();
}
Then you always know what item refers to.

Related

How to make this method more generic

i am having some trouble figuring something regarding inheritance in Java. I thought it would be straightforward but it has stumped me.
I have this superclass..
public class MyItem {
private String barCode;
private String price;
public String getBarCode() {
return barCode;
}
public void setBarCode(String barCode) {
this.barCode = barCode;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}
And i have these 2 subclasses
public class PromotionalItem extends MyItem {
private String promotion;
public String setPromotion(String promotion) {
this.promotion = promotion;
}
public void getPromotion() {
this.promotion = promotion;
}
}
public class SellableItem extends MyItem {
private String quantity;
public String setQuantity(String quantity) {
this.quantity = quantity;
}
public void getQuantity() {
this.quantity = quantity;
}
}
Now i have a method that i want to make generic, i thought something like this could work...
public void processItem(MyItem item){
if(item.getClass().isAssignableFrom(PromotionalItem.class)){
processPromotionalItem((PromotionalItem)item);
}
else{
processSellableItem((SellableItem)item);
}
}
But I am getting a ClassCastException when i try to cast these items as their respective subclasses. I thought something like this would be do-able. Am i missing something? What is the alternative do something like this?
The code looks like an anti-pattern. What I would do is have an abstract method called process in MyItem and have both subclasses implementing that method:
public class MyItem {
private String barCode;
private String price;
public String getBarCode() {
return barCode;
}
public void setBarCode(String barCode) {
this.barCode = barCode;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public abstract void process();
}
Now if you have a subclass you are forced to implement the process method, and then instead of checking what class it is you can just call the process method directly.
public void processItem(MyItem item){
item.process();
}
Make MyClass abstract and add an abstract process method, like #JoakimDanielson suggested. Then, in your child classes, override that method and implement your own logic.
public abstract class MyItem {
...
public abstract void process();
}
public class PromotionalItem extends MyItem {
...
#Override
public void process() {
// do whatever
}
}
public class SellableItem extends MyItem {
...
#Override
public void process() {
// do whatever
}
}
Then, in your processItem method, just call process:
public void processItem(MyItem item) {
item.process();
}
In your case you should use instanceof instead of isAssignableFrom (be careful though, the syntax is different, more on that below).
isAssignableFrom checks if the parameter object can be written to the object the function has been called from. instanceof checks if the left object is from the same class or a subclass of the right class. This will make more sense once you've seen the syntax of instanceof:
if(item instanceof PromotionalItem){
processPromotionalItem((PromotionalItem)item);
}
So in a nutshell, your logic was just a little off. You were trying to cast from one subclass of your item class to a completely different subclass.
Use the instanceof keyword
if(item instanceof PromotionalItem){
processPromotionalItem((PromotionalItem) item);
} else if(item instanceof SellableItem) {
processSellableItem((SellableItem) item);
}
Make sure you use else if not only else because item might be
something else other than PromotionalItem and SellableItem if you
cast it to a class from which it wasn't build from, you will get a ClassCastException
instanceof is a keyword that is used for checking if a reference variable is containing a given type of object reference or not.
I‘ll only address the issue, as there are enough solutions (I think #Major Ben s one is nice)
item.getClass().isAssignableFrom(PromotionalItem.class)
What this line means is:
„Can I assign to the dynamic class of item an instance of PromotionalItem.“
But now consider this - is it legal?
MyItem item = new PromotionalItem();
Yes, it is. So this will always be true. Hence, you then try to cast to PromotionalItem, even when it is actually not ok.
Also have a look at this. https://stackoverflow.com/a/3657960/2995907
Using abstract class which is great. We can think also with generices like
public class MyItem<T extends MyItem> {
private String barCode;
private String price;
public String getBarCode() { return barCode; }
public void setBarCode(String barCode) { this.barCode = barCode; }
public String getPrice() { return price; }
public void setPrice(String price) { this.price = price; }
public void process(T item) {
if(item instanceof PromotionalItem){
System.out.println("PromotionalItem");
//do something for promotionalItem
} else if(item instanceof SellableItem) {
System.out.println("SellableItem");
//do something for SellableItem
}
}
}
public class PromotionalItem extends MyItem {
private String promotion;
public void setPromotion(String promotion) {
this.promotion = promotion;
}
public String getPromotion() {
return promotion;
}
}
public class SellableItem extends MyItem {
private String quantity;
public void setQuantity(String quantity) {
this.quantity = quantity;
}
public String getQuantity() {
return quantity;
}
}
#Test
public void test_porecessItem() {
PromotionalItem promotionalItem = new PromotionalItem();
SellableItem sellableItem = new SellableItem();
MyItem<PromotionalItem> promotionalItemMyItem = new MyItem<>();
MyItem<SellableItem> sellableItemMyItem = new MyItem<>();
promotionalItem.process(promotionalItem);
sellableItemMyItem.process(sellableItem);
}
By the way, this is just an option which we can think.

How to get the Enum value in serialised JSON from a Java object

I have a Gender enum (which I can't edit) which is used in a Java class. Now, when I need to Serialise the Java class to JSON, I want the value of the Enum as part of the JSON and not the Enum name. For example below in my enum, and when this enum is serialized, I want the value as {"gender":"Male"} I am using:
String underWritingJSONString = objectMapper.writeValueAsString(myObject);
public enum Gender {
MALE("Male"),
FEMALE("Female");
Gender(String gender) {
this.name = gender;
}
private String name;
String toValue() {
return name;
}
}
expected result = {"gender":"Male"}
current result = {"gender":"MALE"}
Following is the sample class
public class MyObject {
#JSONField
public Gender gender;
public MyObject() {
}
public Gender getGender() {
return this.gender;
}
}
If enum has toString() method which returns the value then
mapper.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING);
Is enough. But your enum don't have that as #Dinesh Kondapaneni mentioned you shold write a custom serializer like
class GenderSerializer extends JsonSerializer<Gender> {
#Override
public void serialize(Gender value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (null == value) {
} else {
try {
gen.writeString(value.toValue());
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}
}
And use it in your MyObject as
#JsonSerialize(using = GenderSerializer.class)
public Gender gender;
FYI: Am using jackson here
You need to add a method with annotation #JsonValue:
enum Gender {
MALE("Male"),
FEMALE("Female");
Gender(String gender) { // constructor
this.name = gender;
}
private String name; // variable
#JsonValue
String toValue() {
return name;
}
}
This method will be called when you are serializing your object to JSON.
(Jackson 2.6.2 and above) you can now simply write:
public enum Gender {
#JsonProperty("Male")
MALE,
#JsonProperty("Female")
FEMALE
}

Deserialize an object's property which has an inconsistent name?

Using Retrofit here to consume Google Civic API.
The library requires you to create a model of what the API will return as I have done already with Election. Which is basically a copy of the google documentation.
(Retrofit binds the response properties to properties with the same name)
Election.Java :
public class Election {
private long id;
private String name;
private String electionDay;
private String ocdDivisionId;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getElectionDay() {
return electionDay;
}
public void setElectionDay(String electionDay) {
this.electionDay = electionDay;
}
public String getOcdDivisionId() {
return ocdDivisionId;
}
public void setOcdDivisionId(String ocdDivisionId) {
this.ocdDivisionId = ocdDivisionId;
}
}
But Representatives have an inconsistent property name, thus I don't see a way to model this in a way Retrofit will know how to deserialize the API's response.
Representatives object (JSON) :
property name is called (key)
How do I let Retrofit deserialize a model that captures the property named variable after a key of the division?
Assuming you're using a Gson converter, I personally would use a map. I guess the same can be achieved with other converters, but I never used them. Say you have the following object:
public class Division {
#SerializedName("name")
#Expose
private String name;
#SerializedName("alsoKnownAs")
#Expose
private List<String> alsoKnownAs = new ArrayList<>();
#SerializedName("officeIndices")
#Expose
private List<Integer> officeIndices = new ArrayList<>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getAlsoKnownAs() {
return alsoKnownAs;
}
public void setAlsoKnownAs(List<String> alsoKnownAs) {
this.alsoKnownAs = alsoKnownAs;
}
public List<Integer> getOfficeIndices() {
return officeIndices;
}
public void setOfficeIndices(List<Integer> officeIndices) {
this.officeIndices = officeIndices;
}
}
Which represents the object inside the divisions array. You can then have the class:
private class Divisions {
#SerializedName("divisions")
#Expose
private Map<String, Division> divisions = new HashMap<>();
// ...
}
Notice the usage of a map here? Behind the scenes Gson will be able to serialise and deserialise your objects. The class Divisions is the root of the json you gave us in the question.
Hope this helps

Assign one json value for two fields in java using GSON

I am trying to assign the value returned by some function to a field in the deserialized class of json.
FileInfo.java
public class FileInfo {
#SerializedName("Name")
private String mName;
#SerializedName("Url")
private String mUri;
#SerializedName("Size")
private Integer mSize;
#SerializedName("ModTime")
private Long mModifiedTime;
private FileType mType;
#SerializedName("Children")
private ArrayList<FileInfo> mChildren = new ArrayList<>();
public ArrayList<FileInfo> getChildren() {
return mChildren;
}
public long getModifiedTime() {
return mModifiedTime;
}
public String getName() {
return mName;
}
public Integer getSize() {
return mSize;
}
public String getUrl() {
return mUri;
}
public FileType getType() {
return mType;
}
public void setChildren(ArrayList<FileInfo> mChildren) {
this.mChildren = mChildren;
}
public void setModifiedTime(long mModifiedTime) {
this.mModifiedTime = mModifiedTime;
}
public void setName(String mName) {
this.mName = mName;
}
public void setSize(Integer mSize) {
this.mSize = mSize;
}
public void setType(FileType mType) {
this.mType = mType;
}
public void setUri(String mUri) {
this.mUri = mUri;
}
#Override
public String toString() {
return FileInfo.class.toString();
}
public FileInfo() {
}
}
The mType needs to be assigned to foo(mName). I looked up custom deserializers and instance creators but none of those helped. I also thought of TypeAdapters which i feel defeats the purpose of keeping deserialization(using GSON) simple.
This is a sample JSON string that will be deserialized.
[
{
"Name":"Airport",
"Url":"http://192.168.2.2/api/sites/Baltimore%20Airport/Airport",
"Size":0,
"ModTime":"2015-12-02T14:19:17.29824-05:00",
"Children":null
}
]
P.S. I'm not sure if this should be done during deserialization but trying anyways. Also please let me know of alternative ways to achieve this.

Custom BeanUtils Converter not called

I am trying to write my own bean utils converter so that I can export my object to a plain text file
I have the main class
public class BeanUtilsTest {
public static void main(String[] args) {
try{
MyObject myObject = new MyObject();
myObject.setId(3l);
myObject.setName("My Name");
ConvertUtilsBean cub = new ConvertUtilsBean();
cub.deregister(String.class);
cub.register(new MyStringConverter(), String.class);
cub.deregister(Long.class);
cub.register(new MyLongConverter(), Long.class);
System.out.println(cub.lookup(String.class));
System.out.println(cub.lookup(Long.class));
BeanUtilsBean bub = new BeanUtilsBean(cub, new PropertyUtilsBean());
String name = bub.getProperty(myObject, "name");
System.out.println(name);
String id = bub.getProperty(myObject, "id");
System.out.println(id);
}catch(Exception ex){
ex.printStackTrace();
}
}
}
The Long Converter
public class MyLongConverter implements Converter{
#Override
public Object convert(Class clazz, Object value) {
System.out.println("Long convert");
return value.toString()+"l";
}
}
The String Converter
public class MyStringConverter implements Converter{
#Override
public Object convert(Class clazz, Object value) {
System.out.println("String convert");
return value.toString()+":";
}
}
Finally my object
public class MyObject {
Long id;
String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
The Output
String convert
My Name:
String convert
3:
I was expecting the id will go through MyLongConverter, but it seems it is still going thru the String one. Why and how can I fix this?
Please advise
Thanks
String id = bub.getProperty(myObject, "id");
Above getProperty function in BeanUtilBean class has to return String representation of the property you requested, regardless of what format the property is defined. So, it will always use String converter (MyStringConverter).
Since the destination type here is always String, MyLongConverter will never be used.
Instead, MyStringConverter should inspect the type of the value parameter and accordingly convert it to String.

Categories