2010-12-28 2 views
11

Я хочу сериализовать свой примерный класс ниже в JSON, используя GSON.Как сериализовать карту карты с помощью 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, или я делаю что-то неправильно здесь?

+2

Если другие ответы правильный и по умолчанию Map serializer не будет обрабатывать вложенные карты, я бы подал отчет об ошибке для команды GSON. Мне кажется, это действительно плохие дефолты для меня; по крайней мере, это должно быть исключение, вместо того, чтобы спокойно глотать содержимое. – StaxMan

+0

+1 @StaxMan похоже. Я видел это раньше, придумал некоторую работу. Если есть какой-то способ сделать это, это стоит документации. – Nishant

ответ

8

Причина Nishant's answer работает потому, что конструктор по умолчанию Gson в позволяет все такие вещи по-умолчанию, что вы в противном случае пришлось бы вручную enably с помощью 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.С аннотации. Вы можете включить Gson для использования этой аннотации через GsonBuilder.setVersion (double).
  • Политика именования полей по умолчанию для вывода Json такая же, как и в Java. Таким образом, поле версии versionNumber класса Java будет выводиться как «versionNumber @» в Json. Те же правила применяются для отображения входящих Json в классы Java. Вы можете изменить эту политику с помощью GsonBuilder.setFieldNamingPolicy (FieldNamingPolicy).
  • By по умолчанию, Gson исключает временные или статические поля из рассмотрения для сериализации и десериализации. Вы можете изменить это поведение с помощью GsonBuilder.excludeFieldsWithModifiers (INT).

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

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

  1. Implement your own Map serializer/deserializer
  2. Используйте более сложную версию метода, что-то вроде этого:

    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

Извините, мой первый комментарий к ответу Нишанта был неправильным. Его предложение не работает полностью для меня. – asmaier

+0

@asmaier ok, добавили еще немного контента к моему ответу –

+0

странно, если вы замените LinkedHashMap с ImmutableMap, то работа прекрасна. – Nishant

4

Попробуйте это:

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" , "Диапазон": { "Start_Time": "Сейчас", "END_TIME": "никогда"}}}


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

+1

Это работает, но почему? Также это немного неудобно. Если я хочу добавить больше карт в мой класс. Пример, мне нужно написать в моей функции toJSON() что-то вроде 'String out = gson.toJson (this.General); выход + = gson.toJson (this.Map2); выход + = gson.toJson (this.map3); ... return out; ' – asmaier

+0

не уверен друг. У меня был этот вопрос, я пошел по этому маршруту. Я предположил, что причиной этого является http://sites.google.com/site/gson/gson-user-guide#TOC-Nested-Classes-including-Inner-Clas – Nishant

+0

Извините, мой первый комментарий был неправильным, и я не могу редактировать это больше. Использование 'gson.toJson (General)' возвращает '{" Version ":" 0.1 "," Range ": {" Start_Time ":" bla "," End_Time ":" blu "}," Checksum ":" + 1 " } '. Но я хочу иметь '{" General ": {" Version ":" 0.1 ", ....'. поэтому ваше решение не работает полностью для меня. – asmaier

1

Более простой альтернативой было бы используйте Джексон вместо 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": "никогда"}}

Смежные вопросы