Saturday, 8 August 2020

Gson: Converting integers to doubles

 Recently I faced this problem. I have data in the below format.

[{

         "a": 1,

         "b": 2

}, {

         "c": 3,

         "d": 4

}]

 

When I am trying to convert the above data to ‘List<Map<String, Object>>’, gson converting integers to real values like below.

 

[{

         "a": 1.0,

         "b": 2.0

}, {

         "c": 3.0,

         "d": 4.0

}]

 

As you observe above snippet, 1 is converted to 1.0, 2 is converted to 2.0 etc.,

 

App.java

package com.sample.app.util;

import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

public class App {
	public static void main(String args[]) {
		String json = "[{\"a\":1,\"b\":2},{\"c\":3,\"d\":4}]";

		Gson gson = new Gson();

		List<Map<String, Object>> list = gson.fromJson(json, new TypeToken<List<Map<String, Object>>>() {
		}.getType());
		for (Map<String, Object> item : list) {
			System.out.println(item);
		}

	}
}

Output

{a=1.0, b=2.0}

{c=3.0, d=4.0}

 

Root cause of this behavior?

https://github.com/google/gson/blob/master/gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java

 

read() method in ObjectTypeAdapter class is implemented like below.

public Object read(JsonReader in) throws IOException {
    JsonToken token = in.peek();
    switch (token) {
    case BEGIN_ARRAY:
    	......

    case NUMBER:
      return in.nextDouble();
	  	......
    }
  }

As you see above snippet, when a token is of type NUMBER encountered, it is converted to double by default.

 

How to resolve the above problem?

Create a custom deserializer that handles number type correctly.

 

DoubleToIntDeserializer.java

package com.sample.app.util;

import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.google.gson.JsonArray;
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;
import com.google.gson.JsonPrimitive;
import com.google.gson.internal.LinkedTreeMap;

public class DoubleToIntDeserializer implements JsonDeserializer<Map<String, Object>> {

	@Override
	@SuppressWarnings("unchecked")
	public Map<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
			throws JsonParseException {
		return (Map<String, Object>) read(json);
	}

	public Object read(JsonElement jsonElement) {

		if (jsonElement.isJsonArray()) {
			List<Object> list = new ArrayList<Object>();
			JsonArray jsonArray = jsonElement.getAsJsonArray();
			for (JsonElement anArr : jsonArray) {
				list.add(read(anArr));
			}
			return list;
		}

		if (jsonElement.isJsonObject()) {
			Map<String, Object> map = new LinkedTreeMap<String, Object>();
			JsonObject jsonObject = jsonElement.getAsJsonObject();
			Set<Map.Entry<String, JsonElement>> entitySet = jsonObject.entrySet();
			for (Map.Entry<String, JsonElement> entry : entitySet) {
				map.put(entry.getKey(), read(entry.getValue()));
			}
			return map;
		}

		if (jsonElement.isJsonPrimitive()) {
			JsonPrimitive jsonPrimitive = jsonElement.getAsJsonPrimitive();
			if (jsonPrimitive.isBoolean()) {
				return jsonPrimitive.getAsBoolean();
			}

			if (jsonPrimitive.isString()) {
				return jsonPrimitive.getAsString();
			}

			if (jsonPrimitive.isNumber()) {

				Number num = jsonPrimitive.getAsNumber();

				if (Math.ceil(num.doubleValue()) == num.longValue())
					return num.longValue();

				return num.doubleValue();

			}
		}

		return null;
	}

}

Build Gson instance using this deserializer.

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

App.java

package com.sample.app.util;

import java.util.List;
import java.util.Map;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.reflect.TypeToken;

public class App {
	public static void main(String args[]) {
		String json = "[{\"a\":1,\"b\":2},{\"c\":3,\"d\":4}]";

		GsonBuilder gsonBuilder = new GsonBuilder();
		
		gsonBuilder.registerTypeAdapter(new TypeToken<Map<String, Object>>() {
		}.getType(), new DoubleToIntDeserializer());

		Gson gson = gsonBuilder.create();

		List<Map<String, Object>> list = gson.fromJson(json, new TypeToken<List<Map<String, Object>>>() {
		}.getType());
		for (Map<String, Object> item : list) {
			System.out.println(item);
		}

	}
}

Output

{a=1, b=2}

{c=3, d=4}



Previous                                                    Next                                                    Home

No comments:

Post a Comment