Je sais que c'est une question assez ancienne, mais je cherchais une solution pour désérialiser génériquement le JSON imbriqué en un Map<String, Object>
, et je n'ai rien trouvé.
De la façon dont mon désérialiseur yaml fonctionne, il définit par défaut les objets JSON Map<String, Object>
lorsque vous ne spécifiez pas de type, mais gson ne semble pas le faire. Heureusement, vous pouvez l'accomplir avec un désérialiseur personnalisé.
J'ai utilisé le désérialiseur suivant pour désérialiser naturellement tout, par défaut JsonObject
s à Map<String, Object>
et JsonArray
s à Object[]
s, où tous les enfants sont désérialisés de la même manière.
private static class NaturalDeserializer implements JsonDeserializer<Object> {
public Object deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) {
if(json.isJsonNull()) return null;
else if(json.isJsonPrimitive()) return handlePrimitive(json.getAsJsonPrimitive());
else if(json.isJsonArray()) return handleArray(json.getAsJsonArray(), context);
else return handleObject(json.getAsJsonObject(), context);
}
private Object handlePrimitive(JsonPrimitive json) {
if(json.isBoolean())
return json.getAsBoolean();
else if(json.isString())
return json.getAsString();
else {
BigDecimal bigDec = json.getAsBigDecimal();
// Find out if it is an int type
try {
bigDec.toBigIntegerExact();
try { return bigDec.intValueExact(); }
catch(ArithmeticException e) {}
return bigDec.longValue();
} catch(ArithmeticException e) {}
// Just return it as a double
return bigDec.doubleValue();
}
}
private Object handleArray(JsonArray json, JsonDeserializationContext context) {
Object[] array = new Object[json.size()];
for(int i = 0; i < array.length; i++)
array[i] = context.deserialize(json.get(i), Object.class);
return array;
}
private Object handleObject(JsonObject json, JsonDeserializationContext context) {
Map<String, Object> map = new HashMap<String, Object>();
for(Map.Entry<String, JsonElement> entry : json.entrySet())
map.put(entry.getKey(), context.deserialize(entry.getValue(), Object.class));
return map;
}
}
Le désordre à l'intérieur de la handlePrimitive
méthode est de s'assurer que vous n'obtenez jamais qu'un Double ou un Entier ou un Long, et pourrait probablement être mieux, ou au moins simplifié si vous êtes d'accord avec l'obtention de BigDecimals, qui je crois est la valeur par défaut.
Vous pouvez enregistrer cet adaptateur comme:
GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Object.class, new NaturalDeserializer());
Gson gson = gsonBuilder.create();
Et puis appelez-le comme:
Object natural = gson.fromJson(source, Object.class);
Je ne sais pas pourquoi ce n'est pas le comportement par défaut dans gson, car c'est dans la plupart des autres bibliothèques de sérialisation semi-structurées ...
Map<String,Object> result = new Gson().fromJson(json, Map.class);
fonctionne avec gson 2.6.2.