Gson. Deserialize integers as integers and not as doubles

后端 未结 7 1028
我寻月下人不归
我寻月下人不归 2020-12-09 16:46

I have json object with arbitary values inside. And I want to deserialize it in a Map. Everything is ok except converting integers to a doubles. See example:



        
相关标签:
7条回答
  • 2020-12-09 17:23

    JSON only has a single Number type and there is no way for the parser to automatically tell what type to convert it to.

    If you aren't going to use a strongly typed object graph, consider using the JsonElement types:

    JsonObject root = new Gson().fromJson(json, JsonObject.class);
    int num = root.getAsJsonObject("inner_obj").get("num").getAsInt();
    
    0 讨论(0)
  • 2020-12-09 17:28

    Been searching for a solution to the nested Map problem myself and "이종은" above was the first answer that actually helped in the non trivial use cases.

    Since the solution above only handled Number I updated the solution to provide generic parsing capability for String and booleans also, see the updated code below:

    private static class MapDeserializer implements JsonDeserializer<Map<String, Object>> {
    
        public Map<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            Map<String, Object> m = new LinkedHashMap<String, Object>();
            JsonObject jo = json.getAsJsonObject();
            for (Entry<String, JsonElement> mx : jo.entrySet()) {
                String key = mx.getKey();
                JsonElement v = mx.getValue();
                if (v.isJsonArray()) {
                    m.put(key, g.fromJson(v, List.class));
                } else if (v.isJsonPrimitive()) {
                    Number num = null;
                    ParsePosition position=new ParsePosition(0);
                    String vString=v.getAsString();
                    try {
                      num = NumberFormat.getInstance(Locale.ROOT).parse(vString,position);
                    } catch (Exception e) {
                    }
                    //Check if the position corresponds to the length of the string
                    if(position.getErrorIndex() < 0 && vString.length() == position.getIndex()) {
                      if (num != null) {
                        m.put(key, num);
                        continue;
                      }
                    }
                    JsonPrimitive prim = v.getAsJsonPrimitive();
                    if (prim.isBoolean()) {
                        m.put(key, prim.getAsBoolean());
                    } else if (prim.isString()) {
                        m.put(key, prim.getAsString());
                    } else {
                        m.put(key, null);
                    }
    
                } else if (v.isJsonObject()) {
                    m.put(key, g.fromJson(v, Map.class));
                }
    
            }
            return m;
        }
    }
    
    private static class ListDeserializer implements JsonDeserializer<List<Object>> {
    
        public List<Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            List<Object> m = new ArrayList<Object>();
            JsonArray arr = json.getAsJsonArray();
            for (JsonElement jsonElement : arr) {
                if (jsonElement.isJsonObject()) {
                    m.add(g.fromJson(jsonElement, Map.class));
                } else if (jsonElement.isJsonArray()) {
                    m.add(g.fromJson(jsonElement, List.class));
                } else if (jsonElement.isJsonPrimitive()) {
                    Number num = null;
                    try {
                        num = NumberFormat.getInstance().parse(jsonElement.getAsString());
                    } catch (Exception e) {
                    }
                    if (num != null) {
                        m.add(num);
                        continue;
                    }
                    JsonPrimitive prim = jsonElement.getAsJsonPrimitive();
                    if (prim.isBoolean()) {
                        m.add(prim.getAsBoolean());
                    } else if (prim.isString()) {
                        m.add(prim.getAsString());
                    } else {
                        m.add(null);
                    }
                }
            }
            return m;
        }
    }
    
    private static Gson g = new GsonBuilder().registerTypeAdapter(Map.class, new MapDeserializer()).registerTypeAdapter(List.class, new ListDeserializer()).setDateFormat("yyyy-MM-dd HH:mm:ss").create();
    
    0 讨论(0)
  • 2020-12-09 17:30

    To avoid possible ClassCastException, it is better to cast to Number first. In the following code map was deserialized from JSON as a Map with no generics.

    int numberOfPages = ((Number) map.get("number_of_pages")).intValue();
    
    0 讨论(0)
  • 2020-12-09 17:32

    Here is my example, the first part is the definition of the class that has an int type field.

    import com.google.api.client.util.Key;
    
    public class Folder {
    
        public static final String FIELD_NAME_CHILD_COUNT = "childCount";
    
        @Key(FIELD_NAME_CHILD_COUNT)
        public final int childCount;
    
        public Folder(int aChildCount) {
            childCount = aChildCount;
        }
    }
    

    Then the TypeAdapter to convert the number type in Gson to a Java object.

    GsonBuilder gsb = new GsonBuilder();
    
    gsb.registerTypeAdapter(Folder.class, new JsonDeserializer<Folder>() {
    
                @Override
                public Folder deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
    
                    int value = json.getAsJsonObject().get("childCount").getAsJsonPrimitive().getAsInt();
    
                    return new Folder(value);
    
                }
            }
    );
    

    The third part is the test data, and it works.

    String gsonData =  new String("\"folder\":{\"childCount\":0}");
    
    0 讨论(0)
  • 2020-12-09 17:36

    Register type adapter for Map:

       Gson gson = new GsonBuilder()
                .registerTypeAdapter(Map.class, new JsonDeserializer<Map>() {
                    @Override
                    public Map deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
                        LinkedTreeMap<String,Object> m  = new LinkedTreeMap<String, Object>();
                        JsonObject jo = json.getAsJsonObject();
                        for (Map.Entry<String, JsonElement> mx : jo.entrySet()){
                            String key = mx.getKey();
                            JsonElement v = mx.getValue();
                            if(v.isJsonArray()){
                                m.put(key, context.deserialize(v, List.class));
                            }else if(v.isJsonPrimitive()){
                                Object value = v.getAsString();
                                try {
                                    Object numValue = NumberFormat.getInstance().parse((String)value);
                                    if (numValue != null && numValue.toString().equals(value)) {
                                        value = numValue;
                                    }
                                } catch (Exception e) {
                                }
                                m.put(key, value);
                            }else if(v.isJsonObject()){
                                m.put(key,context.deserialize(v, Map.class));
                            }
                        }
                        return m;
                    }
                })
                .create();
    

    and deserialize using it like: gson.fromJson(instanceJson, Map.class), where instanceJson is an json of object which should be deserialized into Map.

    0 讨论(0)
  • 2020-12-09 17:41

    It's my code

    private static class MapDeserializer implements JsonDeserializer<Map<String,Object>> {
        public Map<String,Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)  throws JsonParseException {
            Map<String,Object> m  = new LinkedHashMap<String, Object>();
            JsonObject jo = json.getAsJsonObject();
            for (Entry<String, JsonElement>  mx : jo.entrySet()){
                String key = mx.getKey();
                JsonElement v = mx.getValue();
                if(v.isJsonArray()){
                    m.put(key, g.fromJson(v, List.class));
                }else if(v.isJsonPrimitive()){
                     Number num = null;
                      try {
                          num = NumberFormat.getInstance().parse(v.getAsString());
                      } catch (Exception e) {
                          e.printStackTrace();
                      }
                    m.put(key,num);
                }else if(v.isJsonObject()){
                    m.put(key,g.fromJson(v, Map.class));    
                }
    
            }
          return m;
       }
    }
    
    private static class ListDeserializer implements JsonDeserializer<List<Object>> {
        public List<Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)  throws JsonParseException {
            List<Object> m  = new ArrayList<Object>();
            JsonArray arr = json.getAsJsonArray();
            for (JsonElement jsonElement : arr) {
                if(jsonElement.isJsonObject()){
                    m.add(g.fromJson(jsonElement, Map.class));
                }else if(jsonElement.isJsonArray()){
                    m.add(g.fromJson(jsonElement, List.class));
                }else if(jsonElement.isJsonPrimitive()){
                    Number num = null;
                    try {
                      num = NumberFormat.getInstance().parse(jsonElement.getAsString());
                    } catch (Exception e) {
                    }
                    m.add(num);
                }
            }
          return m;
       }
    }
    
    private static Gson g = new GsonBuilder().registerTypeAdapter(Map.class, new MapDeserializer()).registerTypeAdapter(List.class, new ListDeserializer()).setDateFormat("yyyy-MM-dd HH:mm:ss").serializeNulls().create();
    
    0 讨论(0)
提交回复
热议问题