Gson 问题汇总

使用Gson

gson是google推出的json解析框架,相较于其他json解析框架,它速度更快也更安全(网上有很多资料,这里就不赘述了),在maven项目中使用gson,只需要引入以下配置即可,这里使用的2.8.5版本

 <dependency> 
   <groupId>com.google.code.gson</groupId>
      <artifactId>gson</artifactId>
      <version>2.8.5</version>
 </dependency>

springboot中指定Gson为默认消息转换器

在springBoot中注入定义的HttpMessageConverter 即可(若使用的其他json框架,注入相应的HttpMessageConverter),例如:

@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Bean
    public HttpMessageConverter GsonHttpMessageConverter() {
        GsonHttpMessageConverter gsonHttpMessageConverter = new GsonHttpMessageConverter();
        gsonHttpMessageConverter.setGson(new Gson());//放入Gson实例
        return gsonHttpMessageConverter;
    }

}

自定义Gson实例

在Gson使用过程中,可能遇到多种情况,例如:

  • 需要设定日期类型的格式
  • 处理特殊格式数据
  • 在解析serialVersionUID字段时发生 class declares multiple JSON fields named serialVersionUid
  • json字符串中的int类型,转换后变成了double类型

解决方法

设定日期类型的格式

第一种方式:
使用JsonDeserializer来指定反序列化(解析)方式,与此对应的还有JsonSerializer接口,用来指定对象序列化方式:

//设定Date类型的反序列化(解析)规则
   private static final com.google.gson.JsonDeserializer<Date> deser = new com.google.gson.JsonDeserializer<Date>() {
            @Override
            public Date deserialize(JsonElement json, Type typeOfT,
                                    JsonDeserializationContext context) throws JsonParseException {
                String date = json.getAsString();
                SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
                format.setTimeZone(TimeZone.getTimeZone("GMT"));
                try {
                    return format.parse(date);
                } catch (ParseException exp) {
                    System.err.println(exp.getMessage());
                    return null;
                }
            }
        };
  private final static Gson INSTANCE = new GsonBuilder().serializeNulls().registerTypeAdapter(Date.class, deser).create();//制定Date类型的转换器

第二种方式:

   private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
   private final static Gson INSTANCE = new GsonBuilder().serializeNulls().setDateFormat(DATE_FORMAT).create();//此规则 将用于序列化与反序列化Date对象,如果多次指定此规则,将以最后一次为准

第二种方式=JsonDeserializer+JsonSerializer
如果项目中已经约定好使用统一的日期格式,使用第二种方式更简便易读

处理特殊格式数据

在某场景中,原本是double类型的数据,在上百条数据中,有几条数据因为数据为空,我接收到是"-“,一转换就报类型无法转换错误(因为是爬取数据),我只能自己想办法把”-"转为——(double)0

    @Test
    void contextLoads() {
        String dataJson = "{\"number\" : 30.2, \"amount\" : \"-\"}";
        Gson gson = GsonUtils.getSingleton();
        Map map = gson.fromJson(dataJson, Map.class);
        System.out.println(map);
    }

解决:

        public static final TypeAdapter<Number> NumberFormat = new TypeAdapter<Number>() {
            @Override
            public Number read(JsonReader in) throws IOException {

                if (in.peek() == JsonToken.NULL) {
                    in.nextNull();
                    return 0;
                }
                try {
                    double i = in.nextDouble();
                    return  i;
                } catch (NumberFormatException e) {
                    //如果报数字格式化异常,判断是否为目标字符串
                    if (e.getMessage().contains("-")){
                        Class<JsonReader> jsonReaderClass = JsonReader.class;
                        try {
                            Field peekedString = jsonReaderClass.getDeclaredField("peekedString");
                            peekedString.setAccessible(true);
                            peekedString.set(in,"0.0");//通过反射更改了json字符串中原"-"为0.0
                            double i = in.nextDouble();//更改值后重新计算
                            return  i;
                        } catch (NoSuchFieldException ex) {
                            ex.printStackTrace();
                        } catch (IllegalAccessException ex) {
                            ex.printStackTrace();
                        }
                        return  0.0;
                    }else {
                        throw new JsonSyntaxException(e);
                    }
                }
            }

            @Override
            public void write(JsonWriter out, Number value) throws IOException {
                out.value(value);
            }
        };


  private final static Gson INSTANCE = new GsonBuilder().serializeNulls()
  										 .disableHtmlEscaping()//默认情况下,Gson会转义HTML字符,例如<>等。使用此选项将 Gson配置为直接传递HTML字符
  										 .registerTypeAdapter(Date.class, deser)
  										 .registerTypeAdapter(Double.class, NumberFormat)
               							 .registerTypeAdapter(double.class, NumberFormat)
               							 .create();

由于registerTypeAdapter只注册指定的类型,不会注册相关类型,所以注册Double时,也需要注册double类型,贴出registerTypeAdapter方法源码以及注释:

 /**
   * Configures Gson for custom serialization or deserialization. This method combines the
   * registration of an {@link TypeAdapter}, {@link InstanceCreator}, {@link JsonSerializer}, and a
   * {@link JsonDeserializer}. It is best used when a single object {@code typeAdapter} implements
   * all the required interfaces for custom serialization with Gson. If a type adapter was
   * previously registered for the specified {@code type}, it is overwritten.
   *
   * <p>This registers the type specified and no other types: you must manually register related
   * types! For example, applications registering {@code boolean.class} should also register {@code
   * Boolean.class}.
   *
   * @param type the type definition for the type adapter being registered
   * @param typeAdapter This object must implement at least one of the {@link TypeAdapter},
   * {@link InstanceCreator}, {@link JsonSerializer}, and a {@link JsonDeserializer} interfaces.
   * @return a reference to this {@code GsonBuilder} object to fulfill the "Builder" pattern
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  public GsonBuilder registerTypeAdapter(Type type, Object typeAdapter) {
    $Gson$Preconditions.checkArgument(typeAdapter instanceof JsonSerializer<?>
        || typeAdapter instanceof JsonDeserializer<?>
        || typeAdapter instanceof InstanceCreator<?>
        || typeAdapter instanceof TypeAdapter<?>);
    if (typeAdapter instanceof InstanceCreator<?>) {
      instanceCreators.put(type, (InstanceCreator) typeAdapter);
    }
    if (typeAdapter instanceof JsonSerializer<?> || typeAdapter instanceof JsonDeserializer<?>) {
      TypeToken<?> typeToken = TypeToken.get(type);
      factories.add(TreeTypeAdapter.newFactoryWithMatchRawType(typeToken, typeAdapter));
    }
    if (typeAdapter instanceof TypeAdapter<?>) {
      factories.add(TypeAdapters.newFactory(TypeToken.get(type), (TypeAdapter)typeAdapter));
    }
    return this;
  }

大家不一定会遇到一摸一样的场景,这里只是提供一个思路

解析serialVersionUID字段报错

在项目中,出现:class declares multiple JSON fields named serialVersionUid异常
解决:

  private static final List<String> EXCLUDE = new ArrayList<String>() {{
            add("serialVersionUID");
        }};
 private final static Gson INSTANCE = new GsonBuilder().serializeNulls()
									 .setExclusionStrategies(new ExclusionStrategy() {
									
									                    @Override
									                    public boolean shouldSkipField(FieldAttributes f) {
									                        boolean exclude = false;
									                        try {
									                            exclude = EXCLUDE.contains(f.getName());
									                        } catch (Exception ignore) {
									                        }
									                        return exclude;
									                    }
									
									                    @Override
									                    public boolean shouldSkipClass(Class<?> aClass) {
									                        return false;
									                    }
									                })//制定排除策略
									     .disableHtmlEscaping()//默认情况下,Gson会转义HTML字符,例如<>等。使用此选项将 Gson配置为直接传递HTML字符
  										 .registerTypeAdapter(Date.class, deser)
  										 .registerTypeAdapter(Double.class, NumberFormat)
               							 .registerTypeAdapter(double.class, NumberFormat)
               							 .create();
                
int类型,转换后变成了double类型

参考
Error: Class declares multiple JSON fields named serialVersionUid

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值