Convierta JSON en un mapa usando Gson

1. Introducción

En este tutorial rápido, aprenderemos cómo convertir una cadena JSON en un mapa usando Gson de Google .

Veremos tres enfoques diferentes para lograrlo y discutiremos sus pros y contras, con algunos ejemplos prácticos.

2. Pasando Map.class

En general, Gson proporciona la siguiente API en su clase Gson para convertir una cadena JSON en un objeto :

public  T fromJson(String json, Class classOfT) throws JsonSyntaxException;

A partir de la firma, queda muy claro que el segundo parámetro es la clase del objeto en el que pretendemos analizar el JSON. En nuestro caso, debería ser Map.class :

String jsonString = "{'employee.name':'Bob','employee.salary':10000}"; Gson gson = new Gson(); Map map = gson.fromJson(jsonString, Map.class); Assert.assertEquals(2, map.size()); Assert.assertEquals(Double.class, map.get("employee.salary").getClass());

Este enfoque hará su mejor estimación con respecto al tipo de valor de cada propiedad.

Por ejemplo, los números se convertirán en Double s, verdadero y falso en booleano y los objetos en LinkedTreeMap s.

Sin embargo, si hay claves duplicadas, la coerción fallará y arrojará una JsonSyntaxException.

Y, debido al borrado de tipos, tampoco podremos configurar este comportamiento de coerción. Entonces, si necesitamos especificar los tipos de clave o valor, entonces necesitaremos un enfoque diferente.

3. Usando TypeToken

Para superar el problema del borrado de tipos para los tipos genéricos, Gson tiene una versión sobrecargada de la API :

public  T fromJson(String json, Type typeOfT) throws JsonSyntaxException;

Podemos construir un mapa con sus parámetros de tipo usando TypeToken de Gson . La clase TypeToken devuelve una instancia de ParameterizedTypeImpl que conserva el tipo de clave y valor incluso en tiempo de ejecución :

String jsonString = "{'Bob' : {'name': 'Bob Willis'}," + "'Jenny' : {'name': 'Jenny McCarthy'}, " + "'Steve' : {'name': 'Steven Waugh'}}"; Gson gson = new Gson(); Type empMapType = new TypeToken() {}.getType(); Map nameEmployeeMap = gson.fromJson(jsonString, empMapType); Assert.assertEquals(3, nameEmployeeMap.size()); Assert.assertEquals(Employee.class, nameEmployeeMap.get("Bob").getClass()); 

Ahora, si construimos nuestro tipo de Mapa como Mapa , entonces el analizador seguirá por defecto como vimos en la sección anterior.

Por supuesto, esto todavía recae en Gson por coaccionar a tipos primitivos. Sin embargo, esos también se pueden personalizar.

4. Uso de JsonDeserializer personalizado

Cuando necesitamos un control detallado sobre la construcción de nuestro objeto Map , podemos implementar un deserializador personalizado de tipo JsonDeserializer.

Para ver un ejemplo, supongamos que nuestro JSON contiene el nombre del empleado como clave y su fecha de contratación como valor. Además, supongamos que el formato de la fecha es aaaa / MM / dd , que no es un formato estándar para Gson .

Podemos configurar Gson para analizar nuestro mapa de manera diferente, luego, implementando un JsonDeserializer:

public class StringDateMapDeserializer implements JsonDeserializer { private SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd"); @Override public Map deserialize(JsonElement elem, Type type, JsonDeserializationContext jsonDeserializationContext) { return elem.getAsJsonObject() .entrySet() .stream() .filter(e -> e.getValue().isJsonPrimitive()) .filter(e -> e.getValue().getAsJsonPrimitive().isString()) .collect( Collectors.toMap( Map.Entry::getKey, e -> formatDate(e.getValue()))); } private Date formatDate(Object value) { try { return format(value.getAsString()); } catch (ParseException ex) { throw new JsonParseException(ex); } } } 

Ahora, tenemos que registrarlo en GsonBuilder contra nuestro tipo de destino Map > y construya un objeto Gson personalizado .

Cuando llamamos a la API fromJson en este objeto Gson , el analizador invoca el deserializador personalizado y devuelve la instancia de Map deseada :

String jsonString = "{'Bob': '2017-06-01', 'Jennie':'2015-01-03'}"; Type type = new TypeToken(){}.getType(); Gson gson = new GsonBuilder() .registerTypeAdapter(type, new StringDateMapDeserializer()) .create(); Map empJoiningDateMap = gson.fromJson(jsonString, type); Assert.assertEquals(2, empJoiningDateMap.size()); Assert.assertEquals(Date.class, empJoiningDateMap.get("Bob").getClass()); 

Esta táctica también es útil cuando nuestro mapa puede contener valores heterogéneos y tenemos una idea clara de cuántos tipos diferentes de valores podrían existir.

Para obtener más información sobre un deserializador personalizado en Gson , no dude en consultar el Libro de cocina de deserialización de Gson.

5. Conclusión

En este breve artículo, aprendimos varias formas de construir un mapa a partir de una cadena con formato JSON. Y también discutimos los casos de uso adecuados para estas variaciones.

El código fuente de los ejemplos está disponible en GitHub.