Closed GoogleCodeExporter closed 9 years ago
Here's the patch for this:
in MapTypeAdapter, add this where the value is being parsed
Object value = null;
if (keyAndValueTypes[1] == Object.class) {
value = ParseObjectFromElement.SINGLETON.apply(entry.getValue());
}
if (value == null) {
value = context.deserialize(entry.getValue(), keyAndValueTypes[1]);
}
ParseObjectFromElement and deps are below:
public enum ParseObjectFromElement implements Function<JsonElement, Object> {
SINGLETON;
public Object apply(JsonElement input) {
Object value = null;
if (input == null || input.isJsonNull()) {
value = null;
} else if (input.isJsonPrimitive()) {
JsonPrimitive primitive = input.getAsJsonPrimitive();
if (primitive.isNumber()) {
value = primitive.getAsNumber();
} else if (primitive.isBoolean()) {
value = primitive.getAsBoolean();
} else {
value = primitive.getAsString();
}
} else if (input.isJsonArray()) {
value = Lists.newArrayList(Iterables.transform(input.getAsJsonArray(), this));
} else if (input.isJsonObject()) {
value = Maps.<String,Object>newLinkedHashMap(Maps.transformValues(JsonObjectAsMap.INSTANCE.apply(input.getAsJsonObject()),
this));
}
return value;
}
}
public enum JsonObjectAsMap implements Function<JsonObject, Map<String,
JsonElement>> {
INSTANCE;
private final Field members;
JsonObjectAsMap() {
try {
members = JsonObject.class.getDeclaredField("members");
members.setAccessible(true);
} catch (NoSuchFieldException e) {
throw new UnsupportedOperationException("cannot access gson internals", e);
}
}
@SuppressWarnings("unchecked")
@Override
public Map<String, JsonElement> apply(JsonObject in) {
try {
return (Map<String, JsonElement>) members.get(in);
} catch (IllegalArgumentException e) {
throw new UnsupportedOperationException("cannot access gson internals", e);
} catch (IllegalAccessException e) {
throw new UnsupportedOperationException("cannot access gson internals", e);
}
}
}
Original comment by adrian.f...@gmail.com
on 15 May 2011 at 12:17
If you want to parse an arbitrary map, how about using JsonParser to parse the
JSON into a JsonElement DOM. You can then navigate the DOM and deserialize
using gson.fromJson(JsonElement, Type) method.
Original comment by inder123
on 15 May 2011 at 6:53
[deleted comment]
Implementation of this change would also provide better consistency with the
behavior when simple object fields of type "Object" are deserialized to.
For example, simple deserialization of
{"one":"won","two":2,"three":false}
into
class ObjectThings
{
Object one;
Object two;
Object three;
}
using
ObjectThings objectThings = gson.fromJson(new FileReader("input.json"),
ObjectThings.class);
generates an instance of ObjectThings where the fields "one", "two", and
"three" are populated with instances of String, LazilyParsedNumber, and Boolean
respectively.
However, when deserializing this same JSON using
Type mapStringObjectType = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> mapStringObject = gson.fromJson(new
FileReader("input.json"), mapStringObjectType);
this creates a map with three entries (with keys "one", "two", "three"), where
each entry value is a mostly useless instance of Object, containing of course
no deserialized data. This is an inconsistent result when compared to the
first example I posted. To be consistent, the types of the map entries values
should be String, LazilyParsedNumber, and Boolean, with values "won", 2, false.
It is certainly possible for users to implement a custom deserializer for this
scenario, however doing so somewhat defeats the entire purpose for using an API
like Gson, and, again, the necessity to do so is inconsistent with the
deserialization behavior of the first example I posted.
A very simple alteration to Gson to provide deserialization to Map<String,
Object> consistent with deserialization to ObjectThings {Object one; Object
two; Object three;} is in JsonDeserializationContext.fromJsonPrimitive(Type,
JsonPrimitive, JsonDeserializationContext) to change
objectNavigator.accept(new ObjectTypePair(json.getAsObject(), typeOfT, true),
visitor);
to
objectNavigator.accept(new ObjectTypePair(null, typeOfT, true), visitor);
This change introduces no new failures to the current Gson test suite. (The
test suite currently contains six failures based on date indexes off by one
position. This is not affected by the change I just described.)
An alternative solution to this problem, so as not to affect the current API
behavior, may be the changes described above by Adrian (I don't know, as I
didn't yet go through them), or it may be to add a configuration to GsonBuilder
along the following lines
public GsonBuilder enableLazyJsonPrimitiveMapValueDeserialization() {
registerTypeHierarchyAdapter(Map.class, PRIMITIVES_AS_MAP_VALUES_TYPE_ADAPTER);
return this;
}
with an appropriate implementation of a new MapTypeAdapter type.
If there is any interest amongst the Gson project maintainers for this change,
I'm glad to provide an implementation along with appropriate test cases.
Original comment by Programm...@gmail.com
on 13 Jun 2011 at 5:52
A recent post on StackOverflow.com helps demonstrate the value of implementing
a reasonable solution for issue 325. http://stackoverflow.com/questions/6455303
Solution With Gson without 325 Implemented: Fugly
Solution With Jackson: 1 Line
Original comment by Programm...@gmail.com
on 24 Jun 2011 at 2:21
Fixed in GSON 2.0. I also answered the stackoverflow question.
Original comment by limpbizkit
on 1 Oct 2011 at 4:55
looks great. thanks
Original comment by adrian.f...@gmail.com
on 1 Jan 2012 at 10:57
Original issue reported on code.google.com by
adrian.f...@gmail.com
on 15 May 2011 at 12:15