2010-12-28 22 views
11

Voglio serializzare la mia classe di esempio qui sotto in JSON utilizzando GSON.Come serializzare una mappa di una mappa con GSON?

import com.google.gson.Gson; 
import com.google.gson.GsonBuilder; 
import java.util.LinkedHashMap; 

public class Example 
{ 
    private LinkedHashMap<String,Object> General; 

    private static final String VERSION="Version"; 
    private static final String RANGE="Range"; 
    private static final String START_TIME="Start_Time"; 
    private static final String END_TIME="End_Time"; 

    public Example() { 
     General = new LinkedHashMap<String,Object>(); 
     General.put(VERSION, "0.1"); 

     LinkedHashMap<String,String> Range = new LinkedHashMap<String, String>(); 
     Range.put(START_TIME, "now"); 
     Range.put(END_TIME, "never"); 

     General.put(RANGE, Range); 
    } 

    public String toJSON() { 
     Gson gson = new GsonBuilder().serializeNulls().create(); 
     return gson.toJson(this); 
    } 
} 

mi aspettavo di ottenere il seguente risultato:

{"General":{"Version":"0.1","Range":{"Start_Time":"now","End_Time":"never"}}} 

Ma il richiamo della funzione toJSON() rendimenti

{"General":{"Version":"0.1","Range":{}}} 

Sembra che GSON non può serializzare mappa Range all'interno della mappa General. È una limitazione di GSON o sto facendo qualcosa di sbagliato qui?

+2

Se altre risposte sono corretto e il serializzatore di mappa predefinito non gestirà le mappe nidificate, vorrei presentare una segnalazione di bug per il team di GSON. Mi sembrano davvero pessime le impostazioni predefinite; almeno dovrebbe lanciare un'eccezione invece di deglutire tranquillamente i contenuti. – StaxMan

+0

+1 @StaxMan sembra così. L'ho visto prima, ho avuto qualche soluzione. Se c'è un modo per farlo, vale la pena documentarlo. – Nishant

risposta

8

Il motivo per cui funziona è perché Nishant's answer costruttore di default di GSON consente tutti i tipi di cose per default che si sarebbe altrimenti necessario manualmente enably utilizzando il GsonBuilder.

Dal JavaDocs:

costruisce un oggetto GSON con la configurazione di default. La configurazione predefinita ha le seguenti impostazioni:

  • Il JSON generato dai metodi toJson è in rappresentazione compatta. Ciò significa che tutto lo spazio bianco non necessario viene rimosso. È possibile modificare questo comportamento con GsonBuilder.setPrettyPrinting().
  • Il JSON generato omette tutti i campi null. Si noti che i valori nulli negli array vengono mantenuti poiché si tratta di un elenco ordinato. Inoltre, se un campo non è nullo, ma il suo JSON generato è vuoto, il campo viene mantenuto. Puoi configurare Gson per serializzare i valori nulli impostando GsonBuilder.serializeNulls().
  • Gson fornisce serializzazione e deserializzazione predefinite per Enums, Map, java.net.URL, java.net.URI, java.util.Locale, java.util.Date, java.math.BigDecimal e java.math.BigInteger classi. Se si preferisce modificare la rappresentazione predefinita, è possibile farlo registrando un adattatore di tipi tramite GsonBuilder.registerTypeAdapter (Type, Object).
  • Il formato data predefinito è uguale a java.text.DateFormat.DEFAULT. Questo formato ignora la parte in millisecondi della data durante la serializzazione. È possibile modificare questo invocando GsonBuilder.setDateFormat (int) o GsonBuilder.setDateFormat (String).
  • Per impostazione predefinita, Gson ignora l'annotazione com.google.gson.annotations.Expose. Puoi abilitare Gson per serializzare/deserializzare solo i campi contrassegnati con questa annotazione tramite GsonBuilder.excludeFieldsWithoutExposeAnnotation().
  • Per impostazione predefinita, Gson ignora com.google.gson.annotations.Dall'annotazione. Puoi abilitare Gson per utilizzare questa annotazione tramite GsonBuilder.setVersion (double).
  • Il criterio di denominazione dei campi predefinito per l'uscita Json è lo stesso di Java. Quindi, un campo di classe Java versionNumber verrà emesso come "versionNumber @ quot; in Json. Le stesse regole vengono applicate per il mapping di Json in entrata alle classi Java. È possibile modificare questo criterio tramite GsonBuilder.setFieldNamingPolicy (FieldNamingPolicy).
  • di default, GSON esclude campi transitori o statici dalla considerazione per la serializzazione e deserializzazione. è possibile modificare questo comportamento attraverso GsonBuilder.excludeFieldsWithModifiers (int).

OK, ora ci vedo quale sia il problema. il il serializzatore di mappa predefinito, come previsto, non supporta le mappe nidificate. Come puoi vedere in this source snippet from DefaultTypeAdapters (in particolare se si esegue un debugger) la variabile childGenericType è impostata sul tipo java.lang.Object per qualche misterioso motivo, quindi il tipo di runtime del valore non viene mai analizzato.

due soluzioni, immagino:

  1. Implement your own Map serializer/deserializer
  2. utilizzare una versione più complicata del metodo, qualcosa di simile:

    public String toJSON(){ 
        final Gson gson = new Gson(); 
        final JsonElement jsonTree = gson.toJsonTree(General, Map.class); 
        final JsonObject jsonObject = new JsonObject(); 
        jsonObject.add("General", jsonTree); 
        return jsonObject.toString(); 
    } 
    
+0

Scusa, il mio primo commento alla risposta di Nishant era sbagliato. Il suo suggerimento non funziona completamente per me. – asmaier

+0

@asmaier ok, aggiunto qualche altro contenuto alla mia risposta –

+0

stranamente, se si sostituisce LinkedHashMap con ImmutableMap funziona bene. – Nishant

4

Prova questo:

Gson gson = new Gson(); 
System.out.println(gson.toJson(General)); 

Non sono sicuro se siete ancora alla ricerca di una soluzione, questo funziona per me:

import java.util.LinkedHashMap; 

import com.google.common.collect.ImmutableMap; 
import com.google.gson.Gson; 
import com.google.gson.GsonBuilder; 

public class Example { 
// private static LinkedHashMap<String,Object> General; 
    private ImmutableMap General; 

    private static final String VERSION="Version"; 
    private static final String RANGE="Range"; 
    private static final String START_TIME="Start_Time"; 
    private static final String END_TIME="End_Time"; 

    public Example() { 

     LinkedHashMap<String,String> Range = new LinkedHashMap<String, String>(); 
     Range.put(START_TIME, "now"); 
     Range.put(END_TIME, "never"); 

//  General.put(RANGE, Range); 
     General = ImmutableMap.of(VERSION, "0.1", RANGE, Range); 
    } 

    public String toJSON() { 
//  Gson gson = new GsonBuilder().serializeNulls().create(); 
      Gson gson = new Gson(); 
      return gson.toJson(this); 
    } 

} 

rendimenti: { "Generale": {" versione ":" 0.1" , "Gamma": { "Start_Time": "adesso", "END_TIME": "mai"}}}


Ovviamente si potrebbe usare ImmutableMap.copyOf(your_hashmap)here invece

+1

Funziona, ma perché? Inoltre è un po 'scomodo. Se voglio aggiungere più mappe alla mia classe Esempio, devo scrivere nella mia funzione toJSON() qualcosa come 'String out = gson.toJson (this.General); out + = gson.toJson (this.Map2); out + = gson.toJson (this.map3); ... return out: – asmaier

+0

non sicuro amico. Ho avuto questo problema, sono andato giù per questa strada. Ho presupposto che la causa fosse questo http://sites.google.com/site/gson/gson-user-guide#TOC-Nested-Classes-including-Inner-Clas – Nishant

+0

Spiacente, il mio primo commento era errato e non posso modificare è più Usando 'gson.toJson (Generale)' restituisce '{" Versione ":" 0.1 "," Intervallo ": {" Start_Time ":" bla "," End_Time ":" blu "}," Checksum ":" + 1 " } '. Ma voglio avere '{" Generale ": {" Versione ":" 0.1 ", ....'. quindi la tua soluzione non funziona completamente per me. – asmaier

1

un'alternativa più semplice sarebbe quella di usa Jackson invece di GSON, la serializzazione di una mappa nidificata funziona fuori dalla scatola:

LinkedHashMap<String, Object> general; 
    general = new LinkedHashMap<String, Object>(); 
    general.put("Version", "0.1"); 

    LinkedHashMap<String, String> Range = new LinkedHashMap<String, String>(); 
    Range.put("Start_Time", "now"); 
    Range.put("End_Time", "never"); 

    general.put("Range", Range); 

    // Serialize the map to json using Jackson 
    ByteArrayOutputStream os = new ByteArrayOutputStream(); 
    new org.codehaus.jackson.map.ObjectMapper().writer().writeValue(os, 
      general);  
    String json = os.toString(); 
    os.close(); 

    System.out.println(json); 

uscita:

{ "Versione": "0.1", "Gamma": { "Start_Time": "adesso", "END_TIME": "mai"}}

Problemi correlati