Converti JSON in una mappa utilizzando Gson

1. Introduzione

In questo breve tutorial, impareremo come convertire una stringa JSON in una mappa utilizzando Gson di Google .

Vedremo tre diversi approcci per ottenere ciò e discuteremo i loro pro e contro, con alcuni esempi pratici.

2. Passaggio di Map.class

In generale, Gson fornisce la seguente API nella sua classe Gson per convertire una stringa JSON in un oggetto :

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

Dalla firma, è molto chiaro che il secondo parametro è la classe dell'oggetto che intendiamo analizzare con il JSON. Nel nostro caso, dovrebbe essere 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());

Questo approccio farà la sua migliore ipotesi per quanto riguarda il tipo di valore per ogni proprietà.

Ad esempio, i numeri verranno convertiti in Double s, true e false in Boolean e gli oggetti in LinkedTreeMap s.

Se sono presenti chiavi duplicate, tuttavia, la coercizione fallirà e genererà un'eccezione JsonSyntaxException.

E, a causa della cancellazione del tipo, non saremo in grado di configurare nemmeno questo comportamento di coercizione. Quindi, se dobbiamo specificare i tipi di chiave o valore, avremo bisogno di un approccio diverso.

3. Utilizzo di TypeToken

Per superare il problema della cancellazione del tipo per i tipi generici, Gson ha una versione sovraccarica dell'API :

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

Possiamo costruire una mappa con i suoi parametri di tipo usando TypeToken di Gson . La classe TypeToken restituisce un'istanza di ParameterizedTypeImpl che conserva il tipo di chiave e valore anche in fase di esecuzione :

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()); 

Ora, se noi costruiamo la nostra mappa tipo Map , allora il parser sarà ancora di default come abbiamo visto nel paragrafo precedente.

Naturalmente, questo ricade ancora su Gson per costringere i tipi primitivi. Anche quelli, tuttavia, possono essere personalizzati.

4. Utilizzo di JsonDeserializer personalizzato

Quando abbiamo bisogno di un controllo granulare sulla costruzione del nostro oggetto Map , possiamo implementare un deserializzatore personalizzato di tipo JsonDeserializer.

Per vedere un esempio, supponiamo che il nostro JSON contenga il nome del dipendente come chiave e la data di assunzione come valore. Inoltre, supponiamo che il formato della data sia aaaa / MM / gg , che non è un formato standard per Gson .

Possiamo configurare Gson per analizzare la nostra mappa in modo diverso, quindi, 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); } } } 

Ora dobbiamo registrarlo in GsonBuilder contro il nostro tipo di destinazione Map > e costruisci un oggetto Gson personalizzato .

Quando chiamiamo l' API fromJson su questo oggetto Gson , il parser richiama il deserializzatore personalizzato e restituisce l' istanza Map desiderata :

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()); 

Questa tattica è utile anche quando la nostra mappa può contenere valori eterogenei e abbiamo una buona idea di quanti diversi tipi di valori potrebbero esserci.

Per ulteriori informazioni su un deserializzatore personalizzato in Gson , non esitare a consultare il libro di ricette sulla deserializzazione di Gson .

5. conclusione

In questo breve articolo, abbiamo imparato diversi modi per costruire una mappa da una stringa in formato JSON. E abbiamo anche discusso i casi d'uso appropriati per queste variazioni.

Il codice sorgente per gli esempi è disponibile su GitHub.