Convert json to Map.Entry object using Gson

EASY VERSION

If I ask Gson to convert some valid json to MyMap, it has no problem with this.

public class MyMap{ Map<Long,String> content; } MyMap myMap = gson.fromJson(json, new TypeToken<MyMap>() {}.getType()); 

DIFFICULT VERSION:

How do I get Gson to do the following?

 public class MyDS{ Map<Map.Entry<Long,String>,Map<Long,String>> content; } MyDS myDS = gson.fromJson(json, new TypeToken<MyDS>() {}.getType()); 

An json example if you really need one.

 "content": { "[1, dog]": { "1": "max", "2": "pi", "3": "robot", "4": "catcher", "5": "reaper" }, "[2, cat]": { "6": "black", "7": "white", "8": "meow", "9": "mice", "10": "rat" }, "[3, rabbit]": { "16": "bunny", "17": "ears", "28": "burgerbun", "39": "alice", "50": "tweak" } } 

more notes

For a good measure, I am trying to run unit test, where all I do is read json with Gson, and I get the following error trace:

 at sun.misc.Unsafe.allocateInstance(Native method) java.lang.reflect.Method.invoke!(Native method) com.google.gson.internal.UnsafeAllocator$1.newInstance(UnsafeAllocator.java:48) com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:223) com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:207) com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:40) com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:186) com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.read(MapTypeAdapterFactory.java:145) com.google.gson.Gson.fromJson(Gson.java:861) com.google.gson.Gson.fromJson(Gson.java:826) com.google.gson.Gson.fromJson(Gson.java:775) 

It doesn't matter if the keys "[3, rabbit]" for "{3, rabbit}"

+8
java json android gson
source share
2 answers

Assuming you have a valid JSON content type:

 { "content": { "[1, dog]": { "1": "max", "2": "pi", "3": "robot", "4": "catcher", "5": "reaper" }, "[2, cat]": { "6": "black", "7": "white", "8": "meow", "9": "mice", "10": "rat" }, "[3, rabbit]": { "16": "bunny", "17": "ears", "28": "burgerbun", "39": "alice", "50": "tweak" } } } 

To achieve what you want, you can simply implement your own Map.Entry Deserializer , since it cannot be deserialized from the box, because it is not an array, and {3, rabbit} not a valid JSON object.

That way, your Deserializer can rely on a regular expression to extract the key, and then instantiate AbstractMap.SimpleEntry using the extracted values, for example:

 public class MapEntryDeserializer implements JsonDeserializer<Map.Entry<Long, String>> { /** * Pattern corresponding to: * Starts with [ * <a non empty sequence of digit characters>, * <a non empty sequence of any characters * Ends with ] */ private static final Pattern PATTERN = Pattern.compile("^\\[(\\d+), ?(.+)\\]$"); public Map.Entry<Long, String> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { // Extract the key/value pair from Strings of type [3, rabbit] String value = json.getAsString(); Matcher matcher = PATTERN.matcher(value); if (!matcher.find()) { throw new JsonParseException( String.format("The map entry doesn't have the expected format: %s", value) ); } return new AbstractMap.SimpleEntry<>( Long.valueOf(matcher.group(1)), matcher.group(2) ); } } 

Then I can deserialize the JSON content with

 Type type = new TypeToken<MyDS>() {}.getType(); Gson gson = new GsonBuilder() .registerTypeAdapter(Map.Entry.class, new MapEntryDeserializer()) .create(); MyDS myDS = gson.fromJson(json, type); 
+4
source share

According to the documentation for Map.Entry:

The only way to get a link to a map entry is through an iterator of this collection view.

https://docs.oracle.com/javase/8/docs/api/java/util/Map.Entry.html

This means that you cannot get Map.Entry until you create the source map. To achieve what you want, you need to parse the JSON on the map and then iterate over it to insert it into your MyDS object.

Having said that, depending on your final use, there may be more efficient ways to reorganize / enter data after parsing it.

+1
source share

All Articles