【问题标题】:Make GSON deserialize numbers to integers or doubles使 GSON 将数字反序列化为整数或双精度数
【发布时间】:2014-07-24 06:44:41
【问题描述】:

我很难与 GSON 相处。

我有一个简单的 JSON,我想反序列化为 Map<String,Object>

123 应该被解析为 int(或 long),123.4 应被解析为 float(或 double),这对我来说真的很直观。

另一方面,GSON 一直在创造双打。

我可以告诉 GSON 不要一直滥用 double 吗?

我的实际代码:

Type mapType = new TypeToken<Map<String, Object>>() {}.getType();
GSON gson = new Gson();
Map<String, Object> map = gson.fromJson(someString, mapType);

【问题讨论】:

  • 根本问题是只有一个Number类型。 JSON 没有 int 类型 - 它们都是浮点数,尽可能省略小数位。
  • @McDowell 是的,我知道这一点。但是 GSON 的行为使得反序列化和序列化产生与原始输入不同的输出,这真的很讨厌;例如我有一个 timestamp 字段,原来是 1234567890,在 deser-ser 循环之后是 1.23456789E9
  • 有什么解决办法吗?
  • 根本问题是 gson 无法提供将整数转换为整数的选项。 gson 可以检测整数吗?是的,您可以使用 LongSerializationPolicy.STRING 转换为字符串。为什么没有 LongSerializationPolicy,LONG 策略?我知道这并不像看起来那么简单,因为 JSON 没有整数类型(同一字段在一个实例中可能是整数,而在另一个实例中可能是十进制),但这就是策略的用武之地。

标签: java json gson


【解决方案1】:

以下代码编译和工作:

package test;

import java.lang.reflect.Type;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import com.google.gson.reflect.TypeToken;

public class Main {

    public static void main(String[] args) {
        GsonBuilder builder = new GsonBuilder();
        builder.registerTypeAdapter(Object.class, new MyObjectDeserializer());
        Gson gson = builder.create();
        String array = "[1, 2.5, 4, 5.66]";

        Type objectListType = new TypeToken<ArrayList<Object>>() {}.getType();
        List<Object> obj = gson.fromJson(array, objectListType);

        System.out.println(Arrays.toString(obj.toArray()));
    }

    public static class MyObjectDeserializer implements JsonDeserializer<Object> {

        public Object deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) 
            throws JsonParseException {

          Number num = null;
          try {
              num = NumberFormat.getInstance().parse(json.getAsJsonPrimitive().getAsString());
          } catch (Exception e) {
              //ignore
          }
          if (num == null) {
              return context.deserialize(json, typeOfT);
          } else {
              return num;
          }
      }
    }

}

我的解决方案将首先尝试将字符串解析为数字,如果失败,它将让标准 Gson 反序列化器完成工作。

如果您需要一个非特定语言环境的数字解析器,请使用此方法来解析数字:

private static Number parse(String str) {
    Number number = null;
    try {
        number = Float.parseFloat(str);
    } catch(NumberFormatException e) {
    try {
        number = Double.parseDouble(str);
    } catch(NumberFormatException e1) {
        try {
            number = Integer.parseInt(str);
        } catch(NumberFormatException e2) {
            try {
                number = Long.parseLong(str);
            } catch(NumberFormatException e3) {
                throw e3;
            }       
        }       
    }       
}
    return number;
}

【讨论】:

  • 谢谢。事实上,我将反序列化许多对象和类的完整映射; GSON 做这个文件,我不喜欢它如何处理数字..
  • 我根据您的反馈更新了我的答案,如果我理解正确,那么我认为应该可行
  • 看起来很有希望,但它依赖于语言环境(分隔符),不应该是:-(
  • 你是什么意思区域依赖?我不明白。我通过使用上下文修复了它..应该可以工作。
  • 我的意思是 Numberformat.getInstance() 返回当前默认语言环境的实例。
【解决方案2】:

混合这样的类型(整数和双打)不是一个好方法。由于您使用 Object 作为类型,因此您将无法从地图中同时获取 Integers 和 Doubles。 Gson 决定哪种类型更适合 Object。在您的情况下,它是 Double,因为所有值都可以加倍,但所有值都不能是整数。

如果你真的需要混合类型,尝试使用 Number 类而不是 Object。示例:

public static void main(String[] args){
        String array = "[1, 2.5, 4, 5.66]";
        Gson gson = new Gson();

        Type type = new TypeToken<ArrayList<Number>>() {}.getType();
        List<Number> obj = gson.fromJson(array, type);

        System.out.println(Arrays.toString(obj.toArray()));
    }

输出:[1, 2.5, 4, 5.66]

此时:

public static void main(String[] args){
    String array = "[1, 2.5, 4, 5.66]";
    Gson gson = new Gson();

    Type type = new TypeToken<ArrayList<Object>>() {}.getType();
    List<Object> obj = gson.fromJson(array, type);

    System.out.println(Arrays.toString(obj.toArray()));
}

会给你输出:[1.0, 2.5, 4.0, 5.66]

【讨论】:

  • 谢谢。但我需要一个Object,因为可能存在其他非数字字段 - 就像在任何 JSON 中一样。我只想改变对数字的处理..
【解决方案3】:

如果您没有一定要专门使用 gson 库,您可以使用 jackson 解决这个反序列化问题,如下所示:

new ObjectMapper().readValue(yourJson, new TypeReference<Map<String,Object>>() {});

这是一个完整的 junit 测试,揭示了反序列化整数值的两个库之间的差异:

@Test
public void integerDeserializationTest() throws Exception {

    final String jsonSource = "{\"intValue\":1,\"doubleValue\":2.0,\"stringValue\":\"value\"}";

    //Using gson library "intValue" is deserialized to 1.0
    final String gsonWrongResult = "{\"intValue\":1.0,\"doubleValue\":2.0,\"stringValue\":\"value\"}";
    Map<String,Object> gsonMap = new Gson().fromJson(jsonSource, new TypeToken<Map<String, Object>>() {
    }.getType());
    assertThat(new Gson().toJson(gsonMap),is(gsonWrongResult));


    //Using jackson library "intValue" is deserialized to 1
    Map<String,Object> jacksonMap = new ObjectMapper().readValue(jsonSource, new TypeReference<Map<String,Object>>() {});
    assertThat(new ObjectMapper().writeValueAsString(jacksonMap),is(jsonSource));

}

【讨论】:

    【解决方案4】:

    您可以进行以下更改以解析该数据:

       testData1={"GOAL1":123, "GOAL2":123.45,"GOAL5":1256,"GOAL6":345.98}
    

    下面是你实际解析它的代码。

    Type mapType = new TypeToken<Map<String, Object>>() {
    }.getType();
    String str = prop.getProperty("testData1");
    System.out.println(str);
    Gson gson = new Gson();
    Map<String, Object> map = gson.fromJson(str, mapType);
    for (String key : map.keySet()) {
        String a = null;
        try {
    
            if (map.get(key) instanceof Double) {
                a = "" + map.get(key);
    
            } else {
                if (map.get(key) instanceof String) {
                    a = (String) map.get(key);
    
                } else {
                    a = null;
                }
            }
    
            if (a != null && a.contains(".") && !a.endsWith(".0")) {
    
                // Convert it into Double/Float
            } else {
                // Work as Integer
            }
    
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    
    }
    

    【讨论】:

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