web-dev-qa-db-fra.com

Comment empêcher Gson d'exprimer des entiers sous forme de flottants

Gson a un comportement étrange lorsque j'essaie de convertir une chaîne en json. Le code ci-dessous transforme le brouillon de chaîne 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}
]
47
Mark Murphy

Vous dites à Gson qu'il cherche une liste de cartes des chaînes aux objets, qui dit essentiellement qu'il doit faire une meilleure estimation du type de l'objet. Depuis JSON ne fait pas de distinction entre les champs entiers et à virgule flottante Gson doit par défaut Float/Double pour les champs numériques.

Gson est fondamentalement conçu pour inspecter le type d'objet que vous souhaitez remplir afin de déterminer comment analyser les données. Si vous ne lui donnez aucun indice, cela ne fonctionnera pas très bien. Une option consiste à définir un JsonDeserializer personnalisé, mais il vaut mieux ne pas utiliser de HashMap (et certainement pas utiliser Hashtable!) Et donner à la place à 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, le but 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 battez tout le point de Gson et pourriez tout aussi bien utiliser un analyseur JSON plus simpliste.

35
dimo414

Cela marche:

 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();
21
Martin Wickman

Fondamentalement, il n'y a pas de réponse parfaite à ce problème. Toutes les "solutions" ne fonctionnent que dans certains cas. Il s'agit d'un problème signalé à l'équipe gson, malheureusement, ils semblent insister sur le fait que "javascript n'a pas de type entier" comme s'ils ne se rendaient pas compte que gson est pour Java pas javascript. Ils ont donc refusé de le corriger) jusqu'à aujourd'hui (2018 maintenant), malgré d'autres lib comme jackson n'a pas de problème du tout, malgré la facilité de le résoudre. Vous devrez donc peut-être résoudre le problème vous-même à partir du code source de gson et créer votre propre gson.jar. le fichier est gson/src/main/Java/com/google/gson/internal/bind/ObjectTypeAdapter.Java

case NUMBER:
   return in.nextDouble();
6
Leon

Je suis en retard à la fête, mais je suis juste tombé dessus moi-même. Dans mon cas, je ne voulais pas spécifier un type Integer dans ma ArrayList - car il pourrait s'agir d'une chaîne ou d'un entier.

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 remplacé la sérialisation Double.class pour renvoyer 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 a testé la valeur des attributs spécifiques à chaque type de données et a renvoyé 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 du haut de ma tête, mais cela devrait vous mettre sur la bonne voie.

5
Steve Kallestad