Google gson fromJson で、 Map<String, Object> への変換は落とし穴。。。

Google gson fromJson を使用する場合、通常変換先は自分で用意するクラスがほとんどのケースであり、
であり、Object に変換させることはあまりやらない。

Gson gson = new GsonBuilder().serializeNulls().create();

Map<String, Object> map = gson.fromJson("{a:0}", new TypeToken<Map<String, Object>>(){}.getType());

System.out.println(map);

この結果は、

{a=0.0}

に、なってしまう。 0 では、なく 0.0 になってしまう。

Map<String, String> map = gson.fromJson("{a:0}", new TypeToken<Map<String, String>>(){}.getType());

であれば、、、

{a=0}

で思いどおりではあるが、Map の value を Objectにしてしまうと、こうならならない。

それでは、Gson のデシリアライザを用意するということで、以下のデシリアライザを用意する。

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParseException;
/**
 * GenericMapDeserializer<T>  :  Map<String, T> へのデシリアライザ
 */
public final class GenericMapDeserializer<T> implements JsonDeserializer<Map<String, T>>{
   @Override
   public Map<String, T> deserialize(JsonElement jsonElement, Type typeOfT
, JsonDeserializationContext context) throws JsonParseException{

      if (!jsonElement.isJsonObject()){
         return null;
      }
      JsonObject jsonObject = jsonElement.getAsJsonObject();
      Set<Entry<String, JsonElement>> jsonEntrySet = jsonObject.entrySet();
      Map<String, T> deserializedMap = new HashMap<String, T>();

      for(Entry<String, JsonElement> entry : jsonEntrySet){
         try{
            if(entry.getValue().isJsonNull()){
               deserializedMap.put(entry.getKey(), null);
            }else if(entry.getValue().isJsonArray()){
               deserializedMap.put(entry.getKey(), (T)entry.getValue());
            }else if(entry.getValue().isJsonObject()){
               deserializedMap.put(entry.getKey(), (T)entry.getValue());
            }else if(entry.getValue().isJsonPrimitive()){
               deserializedMap.put(entry.getKey(), context.deserialize(entry.getValue(), String.class));
            }
         }catch(Exception e){
            throw new RuntimeException(e.getMessage(), e);
         }
      }
      return deserializedMap;
   }
}

これを GsonBuilder で Gson を作るとき、registerTypeAdapter で指定する。

Gson gson = new GsonBuilder()
.registerTypeAdapter(new TypeToken<Map<String, Object>>(){}.getType(),new GenericMapDeserializer<Object>())
.serializeNulls()
.create();

こうして、、

Map<String, Object> map = gson.fromJson("{a:0}", new TypeToken<Map<String, Object>>(){}.getType());

System.out.println(map);

とすれば、、、

{a=0}

が結果になる。