Je dois convertir json string en objet Java et l'afficher en tant que long. La chaîne json est un tableau fixe de nombres longs:
{numbers
[ 268627104, 485677888, 506884800 ] }
Le code à convertir fonctionne correctement dans tous les cas, à l'exception des nombres se terminant par 0. Il convertit ceux-ci en un format numérique:
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;
}
Résultat réel: Objet Java: 268627104, 485677888, 5.068848E + 8
Notez que le dernier numéro est converti en format de notation scientifique. Quelqu'un peut-il suggérer ce qui pourrait être fait pour contourner le problème, le prévenir ou l'annuler? J'utilise Gson v1.7.1
Si la sérialisation en chaîne est une option pour vous, vous pouvez configurer GSON pour le faire avec:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.setLongSerializationPolicy( LongSerializationPolicy.STRING );
Gson gson = gsonBuilder.create();
Cela produira quelque chose comme:
{numbers : [ "268627104", "485677888", "506884800" ] }
Une autre solution consiste à utiliser la classe JsonParser à la place. Cela renverra les représentations d'objet Gson (JsonElement) plutôt qu'une classe définie par l'utilisateur, mais évite le problème de la conversion en notation scientifique.
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);
}
}
Sortie de code:
Gson output:
{numbers=[2.68627104E8, 4.85677888E8, 5.068848E8]}
JsonParser output:
{"numbers":[268627104,485677888,506884800]}
J'ai eu un problème similaire, et non seulement il convertit les entiers en doubles, mais il perd en fait la précision pour certains nombres longs, comme décrit dans cette question liée .
J'ai suivi cette conversion vers la méthode ObjectTypeAdapter
de read
, plus précisément:
case NUMBER:
return in.nextDouble();
Il est peut-être possible de brancher une TypeAdapter
modifiée pour Object
, mais cela n'a pas fonctionné. J'ai donc copié la méthode read
(Object read(JsonReader in)
) dans mon propre code et modifié les lignes ci-dessus en ceci:
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);
J'aimerais que Gson le fasse par défaut ..
Ensuite, je mets les autres pièces de liaison dans une méthode d'assistance qui ressemble à ceci:
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);
}
}
Alors maintenant, au lieu de new Gson().fromJson(r, Object.class)
, j’appelle parse(r)
.
Cela fonctionne bien pour moi car je veux pouvoir analyser les données json avec n'importe quelle structure, mais si vous ciblez une classe particulière, vous devez probablement simplement éliminer les occurrences de Object
au sein des membres de cette classe.
Vous avez le même problème, après une enquête, voici ce que j'ai trouvé.
Le comportement:
Gson
le convertirait en Double
,Jackson
le convertirait en Integer
ou Long
, dépend de la taille du nombre.Solutions possibles:
Gson
de Double
en Long
, explicitement.Jackson
à la place. Code - test pour 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
]
}
Comment courir:
Jackson
& TestNG
.numbers.json
dans le même package que ParseNumberTest.Java
.Sortie:
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
Pas malin, mais la méthode de travail consiste toujours à ajouter "au début et à la fin du numéro. Puis, une fois le traitement terminé, supprimez-le.