Markusbug
Markusbug

Reputation: 61

Gson cannot deserialize same string it created?

I have a HashMap<Picture, String> with Picture being a data-class I created in kotlin. I save the HashMap into the SharedPreferences using gson.toJson(hashmap) and this works fine. But when I try to deserialize the very same string (I checked) into the HashMap<Picture, String> again, it fails with a weird error.

This is the Exception:

java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 3 path $.

This is the string for reference:

{
   "Picture(image_url\u003dhttps://nftmintapp.infura-ipfs.io/ipfs/QmZnbgRFCvqXeahD37vaRANjPiyF9oCC2aWw1TwHat8SaU, creator_name\u003dmarkus, creator_address\u003d0x0, image_name\u003dethOS3, additional_url\u003dhttps://google.com)":"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
}

The String I save in reference to the String is a Bytearray that I convert to string using Base64.encodeToString(bytes, Base64.NO_WRAP).

I assumed that gson would be able to de-serialize anything it serialized itself, has anybody ever encountered this?

Upvotes: 0

Views: 68

Answers (1)

Marcono1234
Marcono1234

Reputation: 6894

The JSON specification only allows strings as property names of JSON objects. Gson works around this by simply calling the toString() function on Map keys and serializing that as JSON property names. However, for deserialization it uses the standard JSON deserialization logic.

For simple Map keys such as String or Int this works just fine, but for complex key types, such as the Picture class in your case, this leads to undesired behavior.

The solution is to either

  • restructure your code so it uses a Map<String, ...> and for example stores the picture data as part of the Picture class as well, or have a data class which contains the picture data and the Picture object
  • or to use GsonBuilder.enableComplexMapKeySerialization(), see also the user guide for additional information

In case you already have released a version of your app with this faulty JSON serialization logic and you want to preserve backward compatibility, you will have to write a JsonDeserializer (or TypeAdapterFactory, but that is a bit more complicated) which tries to deserialize this faulty JSON value.

Upvotes: 1

Related Questions