yasson
yasson copied to clipboard
Map deserialization doesn't use Adapter and generates runtime exception on access
Given a simple POJO "Person" with a String attribute "name" and a test like
public static void main(String[] args) { String json = "{\"foo\" : \"bar\"}"; Map<Person, String> map = JsonbBuilder.create().fromJson(json, new HashMap<Person, String>() {}.getClass().getGenericSuperclass()); map.keySet().forEach(Person::getName); }
I end up with
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to test.Person at java.util.HashMap$KeySet.forEach(HashMap.java:933) at test.Test.main(Test.java:14)
If I register an adapter for Person, it is never called. How can a String even fit into the Person map key and only be detected at runtime?
Looks like a reopen of #110.
In runtime the <Person> signature is not present, so any value fits. However Yasson should avoid class cast failing earlier and also tell something more specific in this case.
A bit of a background on current implementation and why your adapter is not used: JSON doesn't allow anything else than string to be used as a key in a json object structure. Serializing Maps with any different key type than String has to end up as a String key mapping. Currently we use String.valueOf (toString) for serialization of any key type other than string.
This can be customized registering adapter / (de)serializer for whole map type for example:
public class PersonKeyMapSerializer implements JsonbSerializer<Map<Person,String>> {}
Adapters / serializers registered directly for the map key type are not used during Map processing. I am not all against it, but if we do want to introduce it, we would still have to check that to/from type cannot be anything else than string for a key.
Does Gson or Jackson etc have any workaround for this case?
Maybe I should open a new issue, but my one is similar to this...
I have a Map<NOT_A_STRING, ?> and I would like to have a json like this:
"items": [
"item" {
"key": { ... },
"value": { ... }
},
"item" {
"key": { ... },
"value": { ... }
},
]
I tried to build a MapAdapter, but was only able to get the content of "key" and "value" as a string, using jsonb.toJson(e.getValue()...)
Then I tried to use serializers. In this case the @JsonbTypeSerializer can't be used because in the MapSerializer the default constructor is missing and a constructor with a builder is required.
So I've added it to the configuration, it was a little bit complex because of the Builder requirement.
Unfortunately the MapSerializer is based on Map<String, ?> maps. So I've inherited from it and then I've overridden the serializeInternal method. I was not able to serialize the key and the value object, because a class cast exception was thrown.
If it could be useful, I'll build a small project just to better clarify the point.