web-dev-qa-db-fra.com

Comment empêcher Gson de convertir un nombre long (une chaîne json) en format de notation scientifique?

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

25
lini sax

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" ] }
16
Adam

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]}
8
melbyts

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.

5
aditsu

Vous avez le même problème, après une enquête, voici ce que j'ai trouvé.

Le comportement:

  • Gson
    Pour un nombre sans fraction, Gson le convertirait en Double,
  • Jackson
    Pour un nombre sans partie décimale, Jackson le convertirait en Integer ou Long, dépend de la taille du nombre.

Solutions possibles:

  • Convertit la valeur de retour de Gson de Double en Long, explicitement.
  • Utilisez Jackson à la place.
    Je préfère ça.

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:  

  • Le scénario de test est basé sur Jackson & TestNG.
  • Placez numbers.json dans le même package que ParseNumberTest.Java.
  • Exécutez-le en tant que test, le type et la valeur du résultat de l'analyse seront alors imprimés.

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
2
Eric Wang

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.

0
zzcron toby