Как сериализовать карту карты с помощью GSON?

Я хочу сериализовать свой примерный класс ниже в JSON, используя 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);
    }
}

Я ожидал получить следующий результат:

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

Но вызов функции toJSON() возвращает

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

Кажется, что GSON не может сериализовать карту Range внутри карты General. Является ли это ограничением GSON или я делаю что-то неправильно здесь?

Ответ 1

Причина, по которой Nishant answer работает, заключается в том, что конструктор по умолчанию Gson позволяет использовать все типы материалов по умолчанию, которые вам в противном случае пришлось бы вручную использовать с помощью GsonBuilder.

Из JavaDocs:

Создает объект Gson с настройкой по умолчанию. Конфигурация по умолчанию имеет следующие настройки:

  • JSON, генерируемый методами toJson, находится в компактном представлении. Это означает, что все ненужное белое пространство удалено. Вы можете изменить это поведение с помощью GsonBuilder.setPrettyPrinting().
  • Сгенерированный JSON пропускает все поля, которые являются нулевыми. Обратите внимание, что нули в массивах сохраняются как есть, так как массив является упорядоченным списком. Более того, если поле не является нулевым, но его сгенерированный JSON пуст, поле сохраняется. Вы можете настроить Gson для сериализации нулевых значений, установив GsonBuilder.serializeNulls().
  • Gson предоставляет сериализацию и десериализацию по умолчанию для Enums, Map, java.net.URL, java.net.URI, java.util.Locale, java.util.Date, java.math.BigDecimal и java.math.BigInteger классы. Если вы предпочитаете изменять представление по умолчанию, вы можете сделать это, зарегистрировав адаптер типа через GsonBuilder.registerTypeAdapter(Тип, Объект).
  • Формат даты по умолчанию такой же, как java.text.DateFormat.DEFAULT. Этот формат игнорирует миллисекундную часть даты во время сериализации. Вы можете изменить это, вызывая GsonBuilder.setDateFormat(int) или GsonBuilder.setDateFormat(String).
  • По умолчанию Gson игнорирует аннотацию com.google.gson.annotations.Expose. Вы можете включить Gson для сериализации/десериализации только тех полей, помеченных этой аннотацией через GsonBuilder.excludeFieldsWithoutExposeAnnotation().
  • По умолчанию Gson игнорирует com.google.gson.annotations.Since с аннотацией. Вы можете включить Gson для использования этой аннотации через GsonBuilder.setVersion(double).
  • Политика именования полей по умолчанию для вывода Json такая же, как и в Java. Таким образом, поле версии versionNumber класса Java будет выводиться как "versionNumber @" в Json. Те же правила применяются для отображения входящих Json в классы Java. Вы можете изменить эту политику с помощью GsonBuilder.setFieldNamingPolicy(FieldNamingPolicy).
  • По умолчанию Gson исключает переходные или статические поля из соображений сериализации и десериализации. Вы можете изменить это поведение через GsonBuilder.excludeFieldsWithModifiers(int).

ОК, теперь я вижу, в чем проблема. Серийный анализатор карт по умолчанию, как вы ожидали, не поддерживает вложенные карты. Как вы можете видеть в этот исходный фрагмент из DefaultTypeAdapters (особенно если вы перейдете к отладчику) переменная childGenericType установлена ​​в тип java.lang.Object по какой-то загадочной причине, поэтому тип времени выполнения никогда не анализируется.

Два решения, я думаю:

Ответ 2

Попробуйте следующее:

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

Не уверен, что вы все еще ищете решение, это работает для меня:

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

}

возвращает: { "Общее": { "Версия": "0,1", "Диапазон": { "Старт_Тим": "сейчас", "Конечный_Тем": "никогда" }}}


Очевидно, вы могли бы использовать ImmutableMap.copyOf(your_hashmap) здесь вместо

Ответ 3

Более простой альтернативой было бы использовать Джексон вместо GSON, сериализация вложенной карты работает из коробки:

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

Вывод:

{ "Версия": "0,1", "Диапазон": { "Start_Time": "Сейчас", "END_TIME": "никогда" }}