【问题标题】:Gson. Deserialize integers as integers and not as doubles格森。将整数反序列化为整数而不是双精度数
【发布时间】:2013-06-13 15:07:41
【问题描述】:

我有 json 对象,里面有任意值。我想在地图中反序列化它。一切都很好,除了将整数转换为双精度数。见例子:

{"id":1, "inner_obj":{"key":"value","num":666,"map":{"key":"value"}}}

反序列化为this(map.toString()):

{id=1.0, inner_obj={key=value, num=666.0, map={key=value}}}

是否有一些简单的方法可以将“id”和“num”反序列化为整数而不是双精度数?

【问题讨论】:

  • Jackson 在检测地图类型方面做得更好,而且比 GSON 更容易配置:github.com/FasterXML/jackson-databind
  • 该问题明确要求 Gson 实现。请坚持这个问题。
  • 另见this answer 我写了一个类似的问题;问题是您必须将数据解析为Object,然后转换为您需要的任何内容。

标签: java json gson


【解决方案1】:

JSON 中没有整数类型。 1 和 1.0 是一样的。您需要在代码中将 1.0 解析为 1。或者您需要将 JSON 映射到某个 VO 类并显式定义该类的字段类型,以便 GSON 能够理解您要查找的内容。

【讨论】:

  • 但是在 java 中有整数和双精度数。如果我有一个带有整数字段“num”的类,很容易将 {"num":1} 反序列化为 Integer,但是当 gson 将 json 反序列化为 Map 时,它打算使用 Double。所以我正在寻找禁用这种方法的方法。
  • @Moses 定义了一个 Map
  • 地图不应只包含整数。
【解决方案2】:

我自己一直在寻找嵌套 Map 问题的解决方案,上面的“이종은”是第一个真正有助于非平凡用例的答案。

由于上面的解决方案只处理数字,我更新了解决方案以提供对字符串和布尔值的通用解析能力,请参阅下面的更新代码:

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

【讨论】:

  • 这对我帮助很大!我只想指出一个小错误。在 ListDeserializer 中,您应该删除 try{ } 块中的“m.add(num)”,因为无论如何都会在 try/catch 之后添加它。如果您按原样使用代码,它会将每个数字添加到结果中两次。
  • 我认为您可以将对 g.fromJson(...) 的调用替换为 context.deserialize(...) 以避免需要 gson 的静态实例(至少它适用于我对于 gson 2.6.2)
【解决方案3】:

JSON 只有一个 Number 类型,解析器无法自动判断将其转换为哪种类型。

如果您不打算使用强类型对象图,请考虑使用JsonElement 类型:

JsonObject root = new Gson().fromJson(json, JsonObject.class);
int num = root.getAsJsonObject("inner_obj").get("num").getAsInt();

【讨论】:

    【解决方案4】:

    这是我的代码

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

    【讨论】:

      【解决方案5】:

      为避免可能的 ClassCastException,最好先转换为 Number。在以下代码中,map 从 JSON 反序列化为 Map,没有泛型。

      int numberOfPages = ((Number) map.get("number_of_pages")).intValue();
      

      【讨论】:

        【解决方案6】:

        这是我的例子,第一部分是具有int类型字段的类的定义。

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

        然后TypeAdapter将Gson中的数字类型转换为Java对象。

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

        第三部分是测试数据,效果不错。

        String gsonData =  new String("\"folder\":{\"childCount\":0}");
        

        【讨论】:

          【解决方案7】:

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

          并像这样使用它进行反序列化:gson.fromJson(instanceJson, Map.class),其中instanceJson 是应反序列化为Map 的对象的json

          【讨论】:

            【解决方案8】:

            这是我解决问题的方法。我试图尽可能干净地实现它。我没有找到更好的简单解决方案来检查数字是否为整数。

            public final class GSONUtil {
            
                private GSONUtil() {
                }
            
                public static Gson createGson() {
            
                    // @formatter:off
                    return new GsonBuilder()
                            .registerTypeAdapter(Map.class, createMapDeserializer())
                            .registerTypeAdapter(List.class, createListDeserializer())
                            .create();
                    // @formatter:on
                }
            
                private static JsonDeserializer<Map<String,Object>> createMapDeserializer() {
                    return new JsonDeserializer<Map<String,Object>>() {
            
                        @Override
                        public Map<String, Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                                throws JsonParseException {
            
                            return json.getAsJsonObject().entrySet().stream() // stream
                                    .collect(Collectors.toMap(Entry::getKey, (e) -> JSONUtil.deserialize(e.getValue(), context)));
                        }
                    };
                }
            
                private static JsonDeserializer<List<Object>> createListDeserializer() {
                    return new JsonDeserializer<List<Object>>() {
            
                        @Override
                        public List<Object> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
                                throws JsonParseException {
            
                            return StreamSupport.stream(json.getAsJsonArray().spliterator(), false) // stream
                                    .map((e) -> JSONUtil.deserialize(e, context)).collect(Collectors.toList());
                        }
                    };
                }
            
                private static Object deserialize(JsonElement value, JsonDeserializationContext context) {
            
                    if (value.isJsonNull()) {
                        return null;
                    }
                    if (value.isJsonObject()) {
                        return context.deserialize(value, Map.class);
                    }
                    if (value.isJsonArray()) {
                        return context.deserialize(value, List.class);
                    }
                    if (value.isJsonPrimitive()) {
                        return parsePrimitive(value);
                    }
            
                    throw new IllegalStateException("This exception should never be thrown!");
                }
            
                private static Object parsePrimitive(JsonElement value) {
            
                    final JsonPrimitive jsonPrimitive = value.getAsJsonPrimitive();
            
                    if (jsonPrimitive.isString()) {
                        return jsonPrimitive.getAsString();
                    }
            
                    if (jsonPrimitive.isBoolean()) {
                        return jsonPrimitive.getAsBoolean();
                    }
            
                    if (jsonPrimitive.isNumber()) {
                        return parseNumber(jsonPrimitive);
                    }
            
                    throw new IllegalStateException("This exception should never be thrown!");
                }
            
                private static Number parseNumber(JsonPrimitive jsonPrimitive) {
            
                    if (isInteger(jsonPrimitive)) {
                        return jsonPrimitive.getAsLong();
                    }
            
                    return jsonPrimitive.getAsDouble();
                }
            
                private static boolean isInteger(final JsonPrimitive jsonPrimitive) {
                    return jsonPrimitive.getAsString().matches("[-]?\\d+");
                }
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-01-09
              • 2016-02-18
              • 1970-01-01
              • 2014-04-18
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多