Подтвердить что ты не робот

Как запретить Gson выражать целые числа как float

У Gson есть странное поведение, когда я пытаюсь преобразовать строку в json. Приведенный ниже код преобразует черновик в json-ответы. Есть ли способ предотвратить добавление gson '.0 ко всем целым значениям?

ArrayList<Hashtable<String, Object>> responses;
Type ResponseList = new TypeToken<ArrayList<Hashtable<String, Object>>>() {}.getType();
responses = new Gson().fromJson(draft, ResponseList);

draft:
[ {"id":4077395,"field_id":242566,"body":""},
  {"id":4077398,"field_id":242569,"body":[[273019,0],[273020,1],[273021,0]]},
  {"id":4077399,"field_id":242570,"body":[[273022,0],[273023,1],[273024,0]]}
]

responses:
[ {id=4077395.0, body=, field_id=242566.0},
  {id=4077398.0, body=[[273019.0, 0.0], [273020.0, 1.0], [273021.0, 0.0]], field_id=242569.0},
  {id=4077399.0, body=[[273022.0, 0.0], [273023.0, 1.0], [273024.0, 0.0]], field_id=242570.0}
]
4b9b3361

Ответ 1

Вы говорите Gson, что он ищет список карт Strings и Objects, который, по сути, говорит о том, что он делает лучшее предположение о типе Object. Поскольку JSON не различает целочисленные поля и поля с плавающей запятой, Gson должен по умолчанию использовать Float/Double для числовых полей.

Gson в основном построен для проверки типа объекта, который вы хотите заполнить, чтобы определить, как анализировать данные. Если вы не дадите намек, это не сработает. Одним из вариантов является определение пользовательского JsonDeserializer, однако лучше не использовать HashMap (и определенно не использовать Hashtable!) И вместо этого предоставлять Gson дополнительную информацию о типе ожидаемых данных.

class Response {
  int id;
  int field_id;
  ArrayList<ArrayList<Integer>> body; // or whatever type is most apropriate
}

responses = new Gson()
            .fromJson(draft, new TypeToken<ArrayList<Response>>(){}.getType());

Опять же, весь смысл Gson в том, чтобы плавно преобразовывать структурированные данные в структурированные объекты. Если вы попросите его создать почти неопределенную структуру, такую как список карт объектов, вы победите весь смысл Gson и можете использовать более упрощенный анализатор JSON.

Ответ 2

Это работает:

 Gson gson = new GsonBuilder().
        registerTypeAdapter(Double.class,  new JsonSerializer<Double>() {   

    @Override
    public JsonElement serialize(Double src, Type typeOfSrc, JsonSerializationContext context) {
        if(src == src.longValue())
            return new JsonPrimitive(src.longValue());          
        return new JsonPrimitive(src);
    }
 }).create();

Ответ 3

В принципе, нет идеального ответа на этот вопрос. Все "решения" работают только в некоторых случаях. Об этой проблеме сообщается команде gson, к сожалению, похоже, они настаивают на том, что "у javascript нет целочисленного типа", как будто они не понимают, что gson для java, а не javascript. Таким образом, они отказались исправить это до сегодняшнего дня (сейчас 2018), несмотря на то, что другие библиотеки, такие как Джексон, вообще не имеют такой проблемы, несмотря на то, как легко это исправить. Поэтому вам, возможно, придется решить проблему самостоятельно из исходного кода gson и создать свой собственный gson.jar. Исходный файл gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java

case NUMBER:
   return in.nextDouble();

Ответ 4

Я опаздываю на вечеринку, но я просто наткнулся на это сам. В моем случае я не хотел указывать тип Integer в моем ArrayList - поскольку это может быть строка или целое число.

Мое решение таково:

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Double.class,  new JsonSerializer<Double>() {

    public JsonElement serialize(Double src, Type typeOfSrc,
                JsonSerializationContext context) {
            Integer value = (int)Math.round(src);
            return new JsonPrimitive(value);
        }
    });

Gson gs = gsonBuilder.create();

Вместо того, чтобы использовать определение по умолчанию Gson с Gson gs = new Gson();, я переопределил сериализацию Double.class, чтобы вернуть целое число.

В моем случае у меня есть строки и целые числа в моем JSON, но у меня нет двойников, поэтому это не создает проблемы.

Если вам требуется двойное или плавающее значение, я полагаю, что можно было бы добавить некоторую логику, которая проверяет значение атрибутов, специфичных для каждого типа данных, и возвращает соответствующее значение. Что-то вроде

if(/*source has a decimal point*/){
  return new JsonPrimitive(src); 
} else if (/* source has some attribute of a Float */){
  Float value = /*convert the src value from double to a Float */;
  return new JsonPrimitive(value);
} else {
  //it looks like an integer
  Integer value = (int)Math.round(src);
  return new JsonPrimitive(value);
}

Я не знаю, как тестировать или преобразовывать эти типы данных с головы, но это должно привести вас к правильному пути.

Ответ 5

Это работа для меня.

Шаг 1: Скопируйте ObjectTypeAdapter в gson в проект, оставив путь таким же, как в gson Как это

com
  - xxx
    - xxx
com
  - google
    - gson
      - internal
        - bind
          ObjectTypeAdapter

Шаг 2: Изменить ObjectTypeAdapter

case NUMBER:
  return in.nextDouble();

Изменено на

case NUMBER:
  String number = in.nextString();
  try {
    return Long.valueOf(number);
  } catch (NumberFormatException e) {
    return Double.valueOf(number);
  }

OK. Gson будет определять приоритеты ObjectTypeAdapter в проекте.