FastJson序列化隐藏特性

针对训练模型控制台的web后端维护,新增了一个int类型的maxTokenLimit字段,表示调用GPT模型请求允许的TokenSize上限值。后端添加好之后,数据库里面这个字段项没有填充数值,默认是空,所以理论上当maxTokenLimit字段为空的时候,应该传输的Json内容为maxTokenLimit: null ,但是前端那边F12查看,反馈接收到后端传参里面并没有这个字段。


FastJson里面有个序列化项的设置,为了节省存储和网络带宽的占用,默认当字段为空的时候,不对这个字段进行传输,导致前端看不到这个maxTokenLimit字段项。

1.修改数据库表增加对应maxTokenLimit字段

确定字段数据范围,类型为整形,目前已知的GPT模型请求tokenSize有些会到32k,也就是32*1024 = 2^15 = 32768,为后面出了更大的工程上的请求size留出余量,针对Mysql整形数据类型信息查找,我这里使用了MEDIUMINT类型。

图片

对应执行DDL类型的SQL语句,工单审批通过后进行表结构修改

ALTER TABLE model_config  
ADD COLUMN max_token_limit MEDIUMINT comment '最大token长度';

补充问题:我怎么知道我传进去的整形数值存储形式是有符号还是无符号的呢?

回答:其实在MySQL中,MEDIUMINT 数据类型默认是有符号的整数类型,如果你想存储无符号整数类型,你需要在定义列时明确指定 UNSIGNED 关键字。要确定存储在数据库中的 MEDIUMINT 类型是有符号还是无符号,你可以查看表的列定义。下面链接提供了三种方法作为参考:

Mysql查看字段数据类型[1]

我这里采用的是使用数据字典查询的方式:

SELECT COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE 
FROM information_schema.COLUMNS
WHERE TABLE_NAME = 'all_model_config' AND COLUMN_NAME = 'max_token_limit';

查出来的结果如下,发现它是有符号的

图片

2.后端工程补充字段CRUD能力

    @ApiOperation("查询配置")
    @RequestMapping(value = "/all", method = RequestMethod.GET)
    public ModelConfigListVO<ModelConfigVO> getModelConfig() {
        return modelConfigService.getAllModelConfig();
    }

Mybatis修改mapper映射

图片

1.本地debug断点调试,查看日志输出

一路debug显示过程中的maxTokenLimit字段,发现它一直是存在的,并且值为null

图片

并且中途偶然发现了一个奇怪的现象 我在调试的时候加入了一个系统的print函数显示结果和JSON.toString()结果进行对比 第一种输出 ----- 系统的print函数输出

System.out.println(allModelConfigList.toString());

第二种输出------JSON.toString()输出

System.out.println(JSON.toJSONString(allModelConfigList.getModelConfigVOList());

发现上面第一行代码打印出的内容包括maxTokenLimit: null 然而第二行代码打印出的内容不包括maxTokenLimit这个字段

那么问题可能就出在最后的后端JSON网络序列化传输上

2.查询相关JSON序列化配置

发现项目里面添加了配置,使用的是FastJson工具,而不是java默认的json序列化。然后查询FastJson的序列化配置文档,发现有个关于null输出的属性--WriteMapNullValue

图片

询问了GPT如何利用JSON.toJSONString输出空值对应的字段,它提示新增一个SerializerFeature.WriteMapNullValue 参数,让我修改全局配置或者局部配置。

System.out.println(JSON.toJSONString(allModelConfigList.getModelConfigVOList(), SerializerFeature.WriteMapNullValue));

改完执行后发现确实达到了预期的效果,并且这个配置是针对单次调用的局部配置。

其实工程上为了节约资源,利用FastJson默认的设置,不对值为null的字段进行传输,是比较合理的(你猜它为什么叫FastJson?) ,所以我就没有更改它的全局配置。所以最后就交给前端进行兼容性设计了,当我后端确实有这个maxTokenLimit字段,并且当前端发现传输的时候没收到这个maxTokenLimit字段的时候,前端那边就显示为空就行。

不止上面这一点,其实还需要注意利用FastJSON转换的对象需要是能够被识别为bean(具备getter和setter方法的),否则的话会出现转换成空值的可能。比如下面的例子,如果把@Data 注解去掉,然后可能就会输出[]空的列表,猜测内部是调用了对象的get/set方法的,类似于BeanUtils工具也是要依赖Bean的。

import lombok.Data;
@Data
public class Tools {
    String type;
    ChatFunctionDTO function;
    public Tools(String type, ChatFunctionDTO function) {
        this.type = type;
        this.function = function;
    }
}
List<Tools> list = new ArrayList<>();
list.add(new Tools());
System.out.println(JSON.toJSONString(list));

当后端某个字段值为null的时候,如果是利用FastJson工具,它默认的序列化设置是WriteMapNullValue属性为false,也就是为了节省存储和网络带宽的占用,不会对这个字段进行传输,最后导致前端看不到这个maxTokenLimit字段项,这个时候可以交给前端进行兼容性设计,发现传输的时候没收到这个对应字段的时候,前端那边就显示为空就行。

参考资料

[1]

https://deepinout.com/mysql/mysql-questions/249_mysql_how_to_determine_if_a_column_is_unsigned.html: https://link.juejin.cn/?target=https%3A%2F%2Fdeepinout.com%2Fmysql%2Fmysql-questions%2F249_mysql_how_to_determine_if_a_column_is_unsigned.html

原文地址:FastJson序列化隐藏特性 

  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值