85 votes

Comment empêcher Gson d'exprimer les entiers comme des flottants ?

Gson a un comportement étrange lorsque j'essaie de convertir une chaîne de caractères en json. Le code ci-dessous transforme les chaînes de caractères en réponses json. Existe-t-il un moyen d'empêcher Gson d'ajouter le '.0 à toutes les valeurs entières ?

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

50voto

dimo414 Points 7128

Vous dites à Gson qu'il cherche une liste de cartes de chaînes de caractères vers des objets, ce qui signifie essentiellement qu'il doit faire une meilleure supposition quant au type de l'objet. Puisque JSON ne fait pas de distinction entre les champs entiers et les champs à virgule flottante Gson doit utiliser par défaut Float/Double pour les champs numériques.

Gson est fondamentalement construit pour inspecter le type de l'objet que vous voulez remplir afin de déterminer comment analyser les données. Si vous ne lui donnez aucun indice, il ne fonctionnera pas très bien. Une option est de définir un JsonDeserializer personnalisé, mais le mieux serait de ne pas utiliser de HashMap (et surtout pas de Hashtable !) et de donner à Gson plus d'informations sur le type de données qu'il attend.

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());

Encore une fois, l'objectif de Gson est de convertir de manière transparente des données structurées en objets structurés. Si vous lui demandez de créer une structure presque indéfinie comme une liste de cartes d'objets, vous allez à l'encontre de l'objectif de Gson, et vous pourriez tout aussi bien utiliser un analyseur JSON plus simpliste.

40voto

Martin Wickman Points 9628

Cela fonctionne :

 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();

18voto

Seeker Points 361

Il existe une solution fournie par la bibliothèque à partir de la version 2.8.9.

Nous pouvons définir la manière dont l'objet est converti en nombre en utilisant la stratégie setObjectToNumberStrategy.

L'implémentation de LONG_OR_DOUBLE fonctionnera dans ce cas. Peut être utilisé comme

GsonBuilder()
    .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)
    .create()

Voir le lien ci-dessous pour plus de détails

https://github.com/google/gson/pull/1290

13voto

Leon Points 848

En fait, il n'y a pas de réponse parfaite à cette question. Toutes les "solutions" ne fonctionnent que dans certains cas. C'est un problème signalé à l'équipe gson, malheureusement, il semble qu'ils insistent sur le fait que "javascript n'a pas de type entier", comme s'ils ne réalisaient pas que gson est pour java et non pour javascript. Ils ont donc refusé de le corriger jusqu'à aujourd'hui (2018 maintenant), alors que d'autres librairies comme jackson n'ont pas du tout ce problème, même s'il est facile de le corriger. Vous devrez donc corriger le problème vous-même à partir du code source de gson et construire votre propre gson.jar. Le fichier source est gson/src/main/java/com/google/gson/internal/bind/ObjectTypeAdapter.java

case NUMBER:
   return in.nextDouble();

7voto

Steve Kallestad Points 1080

Je suis en retard, mais je viens de rencontrer ce problème. Dans mon cas, je ne voulais pas spécifier un type Integer dans mon ArrayList - puisqu'il pouvait s'agir d'une chaîne de caractères ou d'un Integer.

Ma solution est la suivante :

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();

Plutôt que d'utiliser la définition Gson par défaut avec Gson gs = new Gson(); J'ai surchargé la sérialisation Double.class pour qu'elle renvoie un entier.

Dans mon cas, j'ai des chaînes et des entiers dans mon JSON, mais je n'ai pas de doubles, donc cela ne pose pas de problème.

Si vous avez besoin d'une valeur double ou flottante, je suppose qu'il serait possible d'ajouter une logique qui testerait la valeur pour les attributs spécifiques à chaque type de données et renverrait une valeur appropriée. Quelque chose comme

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);
}

Je ne sais pas comment tester ou convertir ces types de données, mais cela devrait vous mettre sur la bonne voie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X