How to serialize a Map of a Map with GSON? How to serialize a Map of a Map with GSON? json json

How to serialize a Map of a Map with GSON?


The reason why Nishant's answer works is because Gson's default constructor enables all kind of stuff per default that you would otherwise have to manually enably using the GsonBuilder.

From the JavaDocs:

Constructs a Gson object with default configuration. The default configuration has the following settings:

  • The JSON generated by toJson methods is in compact representation. This means that all the unneeded white-space is removed. You can change this behavior with GsonBuilder.setPrettyPrinting().
  • The generated JSON omits all the fields that are null. Note that nulls in arrays are kept as is since an array is an ordered list. Moreover, if a field is not null, but its generated JSON is empty, the field is kept. You can configure Gson to serialize null values by setting GsonBuilder.serializeNulls().
  • Gson provides default serialization and deserialization for Enums, Map, java.net.URL, java.net.URI, java.util.Locale, java.util.Date, java.math.BigDecimal, and java.math.BigInteger classes. If you would prefer to change the default representation, you can do so by registering a type adapter through GsonBuilder.registerTypeAdapter(Type, Object).
  • The default Date format is same as java.text.DateFormat.DEFAULT. This format ignores the millisecond portion of the date during serialization. You can change this by invoking GsonBuilder.setDateFormat(int) or GsonBuilder.setDateFormat(String).
  • By default, Gson ignores the com.google.gson.annotations.Expose annotation. You can enable Gson to serialize/deserialize only those fields marked with this annotation through GsonBuilder.excludeFieldsWithoutExposeAnnotation().
  • By default, Gson ignores the com.google.gson.annotations.Since annotation. You can enable Gson to use this annotation through GsonBuilder.setVersion(double).
  • The default field naming policy for the output Json is same as in Java. So, a Java class field versionNumber will be output as "versionNumber@quot; in Json. The same rules are applied for mapping incoming Json to the Java classes. You can change this policy through GsonBuilder.setFieldNamingPolicy(FieldNamingPolicy).
  • By default, Gson excludes transient or static fields from consideration for serialization and deserialization. You can change this behavior through GsonBuilder.excludeFieldsWithModifiers(int).

OK, now I see what the problem is. The default Map serializer, as you expected, does not support nested maps. As you can see in this source snippet from DefaultTypeAdapters (especially if you step through with a debugger) the variable childGenericType is set to the type java.lang.Object for some mysterious reason, so the runtime type of the value is never analysed.

Two solutions, I guess:

  1. Implement your own Map serializer / deserializer
  2. Use a more complicated version of your method, something like this:

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


Try this:

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

Not sure if you're still looking for a solution, this works for 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);    }}

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


Obviously you could use ImmutableMap.copyOf(your_hashmap)here instead


A simpler alternative would be to use Jackson instead of GSON, serialization of a nested map works out of the box:

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

Output:

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