Как запретить Gson преобразовывать длинное число (строка json) в формат научной нотации?
Мне нужно преобразовать строку json в объект java и отобразить ее как длинную. Строка json является фиксированным массивом длинных чисел:
{numbers
[ 268627104, 485677888, 506884800 ] }
Код для преобразования работает отлично во всех случаях, за исключением чисел, заканчивающихся на 0. Он преобразует их в формат научных обозначений:
public static Object fromJson(HttpResponse response, Class<?> classOf)
throws IOException {
InputStream instream = response.getResponseInputStream();
Object obj = null;
try {
Reader reader = new InputStreamReader(instream, HTTP.UTF_8);
Gson gson = new Gson();
obj = gson.fromJson(reader, classOf);
Logger.d(TAG, "json --> "+gson.toJson(obj));
} catch (UnsupportedEncodingException e) {
Logger.e(TAG, "unsupported encoding", e);
} catch (Exception e) {
Logger.e(TAG, "json parsing error", e);
}
return obj;
}
Фактический результат:
Объект Java: 268627104, 485677888, 5.068848E + 8
Обратите внимание, что последнее число преобразуется в формат научной нотации. Может ли кто-нибудь предложить, что можно сделать, чтобы обойти это или предотвратить его или отменить? Я использую Gson v1.7.1
Ответы
Ответ 1
Если для вас используется сериализация в String, вы можете настроить GSON для этого:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLongSerializationPolicy( LongSerializationPolicy.STRING );
Gson gson = gsonBuilder.create();
Это приведет к чему-то вроде:
{numbers : [ "268627104", "485677888", "506884800" ] }
Ответ 2
Другая работа - использовать вместо этого класс JsonParser. Это вернет представления объектов Gson (JsonElement), а не пользовательский класс, но избегает проблемы преобразования в научную нотацию.
import java.lang.reflect.Type;
import java.util.Map;
import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
public class GsonTest
{
public static void main(String[] args)
{
String json = "{numbers:[268627104,485677888,506884800]}";
Gson gson = new Gson();
Type type = new TypeToken<Map<String, Object>>(){}.getType();
Map<String, Object> jsonMap = gson.fromJson(json, type);
System.out.println("Gson output:");
System.out.println(jsonMap);
JsonParser jsonParser = new JsonParser();
JsonElement jsonElement = jsonParser.parse(json);
System.out.println("JsonParser output:");
System.out.println(jsonElement);
}
}
Выход кода:
Gson output:
{numbers=[2.68627104E8, 4.85677888E8, 5.068848E8]}
JsonParser output:
{"numbers":[268627104,485677888,506884800]}
Ответ 3
У меня была аналогичная проблема, и она не только преобразует целые числа в двойные, но и фактически теряет точность для некоторых длинных чисел, как описано в этом вопросе .
Я отследил это преобразование до метода ObjectTypeAdapter
read
, в частности:
case NUMBER:
return in.nextDouble();
Возможно, можно подключить измененный TypeAdapter
для Object
, но я не мог заставить это работать, поэтому вместо этого я просто скопировал метод read
(Object read(JsonReader in)
) в свой собственный код и изменили приведенные выше строки следующим образом:
case NUMBER:
final String s = in.nextString();
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
// ignore
}
try {
return Long.parseLong(s);
} catch (NumberFormatException e) {
// ignore
}
return Double.parseDouble(s);
Я хотел бы, чтобы Gson сделал это по умолчанию..
Затем я помещаю другие соединительные элементы в вспомогательный метод, который выглядит примерно так:
public static Object parse(final Reader r) {
try (final JsonReader jr = new JsonReader(r)) {
jr.setLenient(true);
boolean empty = true;
Object o = null;
try {
jr.peek();
empty = false;
o = read(jr);
} catch (EOFException e) {
if (!empty) {
throw new JsonSyntaxException(e);
}
}
if (o != null && jr.peek() != JsonToken.END_DOCUMENT) {
throw new JsonIOException("JSON document was not fully consumed.");
}
return o;
} catch (IOException e) {
throw new JsonIOException(e);
}
}
Итак, вместо new Gson().fromJson(r, Object.class)
я вызываю parse(r)
.
Это хорошо работает для меня, потому что я хочу иметь возможность анализировать данные json с любой структурой, но если у вас есть определенный класс, на который вы нацеливаетесь, вам, вероятно, просто нужно устранить появление Object
внутри этого класса.
Ответ 4
У вас такая же проблема, после некоторого расследования здесь я нашел.
Поведение:
- Gson
Для числа без дробной части Gson
преобразует его как Double
,
- Джексон
Для числа без дробной части Jackson
преобразует его как Integer
или Long
, зависит от того, насколько велика цифра.
Возможные решения:
- Преобразовать
Gson
возвращаемое значение от Double
до Long
, явно.
- Вместо этого используйте
Jackson
.
Я предпочитаю это.
Код - тест для Jackson
ParseNumberTest.java:
import java.util.List;
import org.testng.Assert;
import org.testng.annotations.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* test - jackson parse numbers,
*
* @author eric
* @date Jan 13, 2018 12:28:36 AM
*/
public class ParseNumberTest {
@Test
public void test() throws Exception {
String jsonFn = "numbers.json";
ObjectMapper mapper = new ObjectMapper();
DummyData dd = mapper.readValue(this.getClass().getResourceAsStream(jsonFn), DummyData.class);
for (Object data : dd.dataList) {
System.out.printf("data type: %s, value: %s\n", data.getClass().getName(), data.toString());
Assert.assertTrue(data.getClass() == Double.class || data.getClass() == Long.class || data.getClass() == Integer.class);
System.out.printf("%s\n\n", "------------");
}
}
static class DummyData {
List<Object> dataList;
public List<Object> getDataList() {
return dataList;
}
public void setDataList(List<Object> dataList) {
this.dataList = dataList;
}
}
}
numbers.json:
{
"dataList": [
150000000000,
150778742934,
150000,
150000.0
]
}
Как запустить:
- Исходный код основан на
Jackson
и TestNG
.
- Поместите
numbers.json
в тот же пакет, что и ParseNumberTest.java
.
- Выполнить как тест testng, тогда он будет печатать тип и значение результата синтаксического анализа.
Вывод:
data type: java.lang.Long, value: 150000000000
------------
data type: java.lang.Long, value: 150778742934
------------
data type: java.lang.Integer, value: 150000
------------
data type: java.lang.Double, value: 150000.0
------------
PASSED: test
Ответ 5
Не разумный, но все же рабочий метод заключается в том, чтобы добавить "в начале и в конце номера. Затем, после завершения обработки, удалите его.
Ответ 6
Мы можем использовать приведенное ниже кодовое решение для номера Long:
Document doc = documentCursor.next();
JsonWriterSettings relaxed = JsonWriterSettings.builder().outputMode(JsonMode.RELAXED).build();
CustomeObject obj = gson.fromJson(doc.toJson(relaxed), CustomeObject.class);
Ответ 7
Я не нашел решения моей проблемы с номерами форматирования gson, заканчивающимися на 0, до научной нотации. Вместо этого я использовал обход, чтобы преобразовать эту научную нотацию в двойную, которую я форматировал запятыми. "value" - это строка json.
private String formatNumber(String value) {
double dValue = Double.parseDouble(value);
String pattern = "#,###";
DecimalFormat formatter = new DecimalFormat(pattern);
String newNumber = formatter.format(dValue);
return newNumber;
}
Это не отвечает на заданный вопрос, но является дополнительным шагом для решения проблемы, чтобы отображать числа, необходимые системе.