java - quitar - ¿Cómo evitar que Gson convierta un número largo(una cadena json) al formato de notación científica?
quitar formato notacion cientifica excel (6)
Necesito convertir la cadena json en un objeto java y mostrarlo como un largo. La cadena json es una matriz fija de números largos:
{numbers
[ 268627104, 485677888, 506884800 ] }
El código para convertir funciona bien en todos los casos, excepto en los números que terminan en 0. Los convierte a un formato de número de notación científica:
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;
}
El resultado real: objeto Java: 268627104, 485677888, 5.068848E + 8
Observe que el último número se convierte a un formato de notación científica. ¿Alguien puede sugerir qué se podría hacer para solucionarlo, prevenirlo o deshacerlo? Estoy usando Gson v1.7.1
No encontré una solución a mi problema de números de formato gson que terminaban en 0 a notación científica. En su lugar, utilicé una solución alternativa para convertir esta notación científica en un doble que formateé con comas. "valor" es la cadena 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;
}
Esto no responde a la pregunta formulada, pero es un paso adicional para solucionar el problema y mostrar los números que requiere el sistema.
No es inteligente, pero aún así el método de trabajo es agregar "al principio y al final del número. Luego, una vez que el procesamiento haya terminado, elimínelo.
Otra solución es utilizar la clase JsonParser en su lugar. Esto devolverá las representaciones de objetos Gson (JsonElement) en lugar de una clase definida por el usuario, pero evita el problema de la conversión a notación científica.
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);
}
}
Salida de código:
Gson output:
{numbers=[2.68627104E8, 4.85677888E8, 5.068848E8]}
JsonParser output:
{"numbers":[268627104,485677888,506884800]}
Si serializar una cadena es una opción para usted, puede configurar GSON para que lo haga con:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLongSerializationPolicy( LongSerializationPolicy.STRING );
Gson gson = gsonBuilder.create();
Esto producirá algo como:
{numbers : [ "268627104", "485677888", "506884800" ] }
Tengo el mismo problema, después de una investigación aquí es lo que encontré.
El comportamiento:
- Gson
Para un número sin parte fraccionaria,Gson
lo convertiría enDouble
, - Jackson
Para un número sin parte fraccionaria,Jackson
lo convertiría comoInteger
oLong
, depende de qué tan grande sea el número.
Soluciones posibles:
- Convierta el valor de retorno de
Gson
deDouble
aLong
, explícitamente. - Utilice
Jackson
lugar.
Prefiero esto.
Código - prueba de 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;
}
}
}
números.json:
{
"dataList": [
150000000000,
150778742934,
150000,
150000.0
]
}
Como correr:
- El caso de prueba se basa en
Jackson
&TestNG
. - Ponga
numbers.json
en el mismo paquete queParseNumberTest.java
. - Ejecutar como prueba de prueba, luego imprimiría el tipo y el valor del resultado del análisis.
Salida:
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
Tuve un problema similar, y no solo convierte los enteros al doble, sino que en realidad pierde precisión para ciertos números largos, como se describe en esta pregunta relacionada .
Busqué esta conversión al método de read
ObjectTypeAdapter
, específicamente:
case NUMBER:
return in.nextDouble();
Es posible que se pueda conectar un TypeAdapter
for Object
modificado, pero no pude hacer que funcionara, por lo que copié el método de read
( Object read(JsonReader in)
) a mi propio código y modifiqué las líneas anteriores para este :
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);
Ojalá Gson hiciera esto por defecto ...
Luego puse las otras piezas de conexión en un método auxiliar que se parece a esto:
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);
}
}
Así que ahora, en lugar de new Gson().fromJson(r, Object.class)
, llamo parse(r)
.
Esto me funciona bien porque quiero poder analizar los datos json con cualquier estructura, pero si tiene una clase en particular a la que está apuntando, probablemente solo necesite eliminar las ocurrencias de Object
dentro de los miembros de esa clase.