Reputation: 157
Attempting to de-serialize JSON to a Map<K, V> where K is a String and V one of several types (i.e String int or boolean). Using Gson, I tried the following code:
public static void main(String[] args) {
JsonObject json = new JsonObject();
json.addProperty("str", "str-value");
json.addProperty("int", 10);
json.addProperty("bool", true);
// "json" now contains {"str":"str-value","int":10,"bool":true}
Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
Gson gson = new Gson();
Map<String, Object> map = gson.fromJson(json, mapType);
String str = (String) map.get("str"); // cannot cast Object to String
str = String.valueOf(map.get("str")); // results in java.lang.Object@1632847
}
Upvotes: 1
Views: 2372
Reputation: 7737
Technical Answer
If you attach the Gson source code and then debug the program you will find the source of the problem. The problem seems to rest in the Gson code.
Since the value is of type Object your call to fromJson(), the class com.google.gson.ObjectNavigator will be used and comes into this section of code [~line 113]
else if (objTypePair.type == Object.class && isPrimitiveOrString(objectToVisit)) {
visitor.visitPrimitive(objectToVisit); //Target value is the proper value after this call
visitor.getTarget(); //target value is now a blank Object.
where visitor is of type JSonObjectDeserializationVisitor, objectToVisit is your value and objectTypePair is the type it is to become (Object).
Now, The JsonObjectDeserializationVisitor has a flag called constructed which is set to false by default. so when the getTarget() method is called, it checks to see if the constructed flag is true, if not it creates a new instance of the object you are trying to create. since the constructed flag is never set the call to getTarget() returns a blank Object.
The call to getTarget() seems unnecessary in the code and appears to be the source of the problem. visitPrimitive performs the proper action by placing the parsed value into the target variable which would be passed back. This appears to be a bug surrounding the Object type.
This isn't something you could fix unless you are willing to do a custom build of Gson. I would recommend filing this as a report on the Gson forums. There is already one report filed for version 1.5
Work Around
I see two ways you can fix this in the short term.
1) Make the Map of type <String, String>
, though you lose the types you can still get the values back properly
2) Put the values into a bean class first and then Serialize that.
public class myObj{
String myStr;
int myInt;
boolean myBool;
//get and set methods
}
public static void main(String args[]){
MyObj o = new MyObj();
o.setMyStr("str-value");
Gson gson = new Gson();
String json = gson.toJson(o);
o = gson.fromJson(json, MyObj.class);
}
Both defeat the purpose of what you are trying to do, but it is in googles court to fix this bug I believe.
Upvotes: 3