一个由服务端返回的long值改变引发的血案

简介: 一个由服务端返回的long值改变引发的血案

背景:服务端返回信息,其中某ID字段是long型,值是16位。前端拿到后再传给服务端,某ID值发生了改变。

这是因为,JS 中能精准表示的最大整数是:

Math.pow(2, 53),十进制即 9007199254740992

所以我们需要服务端返回给前端时对这种情况进行处理,本文是在springboot环境下使用fastJsonHttpMessageConverter引申介绍。


【1】自定义MyLongSerializerAndDeserializer

我们自定义MyLongSerializerAndDeserializer实现对long型的序列化、反序列化。

import com.alibaba.fastjson.parser.DefaultJSONParser;
import com.alibaba.fastjson.parser.JSONLexer;
import com.alibaba.fastjson.parser.JSONToken;
import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer;
import com.alibaba.fastjson.serializer.JSONSerializer;
import com.alibaba.fastjson.serializer.ObjectSerializer;
import com.alibaba.fastjson.serializer.SerializeWriter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.util.TypeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.lang.reflect.Type;
import java.util.concurrent.atomic.AtomicLong;
/**
 * Created by jianggc at 2021/12/6.
 */
public class MyLongSerializerAndDeserializer implements ObjectSerializer , ObjectDeserializer {
    private static final Logger LOGGER= LoggerFactory.getLogger(MyLongSerializerAndDeserializer.class);
    // 反序列化
    @Override
    public <T> T deserialze(DefaultJSONParser parser, Type clazz, Object fieldName) {
        final JSONLexer lexer = parser.lexer;
        Long longObject;
        if (lexer.token() == JSONToken.LITERAL_INT) {
            long longValue = lexer.longValue();
            lexer.nextToken(JSONToken.COMMA);
            longObject = Long.valueOf(longValue);
        } else {
            Object value = parser.parse();
            if (value == null) {
                return null;
            }
            longObject = TypeUtils.castToLong(value);
        }
        return clazz == AtomicLong.class //
                ? (T) new AtomicLong(longObject.longValue()) //
                : (T) longObject;
    }
    @Override
    public int getFastMatchToken() {
        return JSONToken.LITERAL_INT;
    }
    //序列化
    @Override
    public void write(JSONSerializer serializer, Object object, Object fieldName, Type fieldType, int features) throws IOException {
        SerializeWriter out = serializer.getWriter();
        if (object == null) {
            out.writeNull(SerializerFeature.WriteNullNumberAsZero);
        } else if (fieldType == java.lang.Long.class){
          // 这里,如果是long,我们就返回"实际值"这样的字符串格式
            LOGGER.debug("对long进行处理");
            out.write("\""+object+"\"");
            return;
        }
    }
}

那么我们如何使用MyLongSerializerAndDeserializer呢?两张方式:

  • ① 某个bean的某个字段单独指定;
  • ② 设置在SerializeConfig中


① 某个bean字段单独指定

如下所致,在ID字段上指定@JSONField(serializeUsing = MyLongSerializerAndDeserializer.class),那么只会对该bean该字段处理。

@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@TableName("tb_sys_course")
@ApiModel(value="SysCourse对象", description="课程表")
public class SysCourse implements Serializable {
    private static final long serialVersionUID = 1L;
    @JSONField(serializeUsing = MyLongSerializerAndDeserializer.class)
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    @ApiModelProperty(value = "课程名称")
    private String name;
    //...
}

② 全局配置

如下所示,在配置类中注入如下bean,设置MyLongSerializerAndDeserializer。

@Bean
FastJsonHttpMessageConverter fastJsonHttpMessageConverter(){
    // 添加支持的MediaTypes;不添加时默认为*/*,也就是默认支持全部
    // 但是MappingJackson2HttpMessageConverter里面支持的MediaTypes为application/json
    List<MediaType> fastMediaTypes = new ArrayList<>();
    fastMediaTypes.add(MediaType.APPLICATION_JSON);
    fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
    FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
    FastJsonConfig config = new FastJsonConfig();
    // 设置日期转换格式
    config.setDateFormat("yyyy-MM-dd HH:mm:ss");
    //设置字符集
    config.setCharset(Charset.forName("UTF-8"));
    // 设置SerializerFeature
    config.setSerializerFeatures(
            //输出类名
            SerializerFeature.WriteClassName,
            //输出map中value为null的数据
            SerializerFeature.WriteMapNullValue,
            //json格式化
            SerializerFeature.PrettyFormat,
            //输出空list为[],而不是null
            SerializerFeature.WriteNullListAsEmpty,
            //输出空string为"",而不是null
            SerializerFeature.WriteNullStringAsEmpty
    );
    // 设置自定义序列化器
    config.getSerializeConfig().put(java.lang.Long.class,new MyLongSerializerAndDeserializer());
    converter.setFastJsonConfig(config);
    converter.setSupportedMediaTypes(fastMediaTypes);
    return converter;
}

【2】关于JSON

① 什么是JSON

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
  • JSON 是轻量级的文本数据交换格式
  • JSON 独立于语言
  • JSON 具有自我描述性,更易理解
  • JSON 文件的文件类型是 “.json”
  • JSON 文本的 MIME 类型是 “application/json”
  • JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON。

② JSON实例

创建JSON对象,键为employees,值为含有三个JSON对象的对象数组;

var JSONObject=
{
  "employees": [
    { "firstName":"Bill" , "lastName":"Gates" },
    { "firstName":"George" , "lastName":"Bush" },
    { "firstName":"Thomas" , "lastName":"Carter" }
  ]
}

创建JSON数组对象,也是数组对象;该数组对象是由三个JSON对象组成。

var employees = [
  { "firstName":"Bill" , "lastName":"Gates" },
  { "firstName":"George" , "lastName":"Bush" },
  { "firstName":"Thomas" , "lastName": "Carter" }
];

可以像这样访问数组对象中的第一项:

employees[1].firstName
//返回的值是:
George

可以像这样修改数据:

employees[1].firstName = "Jobs";

③ JSON对象转JavaScript对象

eval( )函数

由于 JSON 语法是 JavaScript 语法的子集,JavaScript 函数 eval() 可用于将 JSON 文本转换为 JavaScript 对象。


eval() 函数使用的是 JavaScript 编译器,可解析 JSON 文本,然后生成 JavaScript 对象。必须把文本包围在括号中,这样才能避免语法错误:

var JSONObject = '{ "employees" : [' +
  '{ "firstName":"Bill" , "lastName":"Gates" },' +
  '{ "firstName":"George" , "lastName":"Bush" },' +
  '{ "firstName":"Thomas" , "lastName":"Carter" } 
]}';

转JS对象:

var obj = eval("("+JSONObject+")");

JSON.parse( )

eval() 函数可编译并执行任何 JavaScript 代码。

这隐藏了一个潜在的安全问题。

使用 JSON 解析器将 JSON 转换为 JavaScript 对象是更安全的做法。

JSON 解析器只能识别 JSON 文本,而不会编译脚本。

在浏览器中,这提供了原生的 JSON 支持,而且 JSON 解析器的速度更快

var obj = JSON.parse(JSONObject);

JSON对象转为string对象

var jsonStr = JSON.stringify(jsonObject)

将表单转为JSON Object

var jsonObject = $('#editform').serializeArray();//JSON Object


目录
相关文章
|
3天前
|
文字识别
印刷文字识别产品使用合集之设置了key值,那么在响应的参数data中,key值对应的信息会按照设置的顺序从0开始一一对应嘛
印刷文字识别(Optical Character Recognition, OCR)技术能够将图片、扫描文档或 PDF 中的印刷文字转化为可编辑和可搜索的数据。这项技术广泛应用于多个领域,以提高工作效率、促进信息数字化。以下是一些印刷文字识别产品使用的典型场景合集。
11 1
|
3天前
|
JSON 前端开发 JavaScript
Long类型字段在前后端传值问题
Long类型字段在前后端传值问题
15 0
|
3天前
四种解决”Arg list too long”参数列表过长的办法
这些方法都可以帮助你避免因参数列表过长而导致的错误。选择方法取决于具体情况和需求。
16 0
|
6月前
|
JSON 小程序 JavaScript
小程序根据返回值的int类型渲染不同的状态
小程序根据返回值的int类型渲染不同的状态
70 0
|
3天前
|
小程序 区块链
血常规常见判断参数
血常规常见判断参数
16 0
|
6月前
|
存储 JavaScript 前端开发
【JS交互埋坑】事件函数自动将数字字符串String转为数值Number
【JS交互埋坑】事件函数自动将数字字符串String转为数值Number
39 0
|
存储 算法 NoSQL
实战:第十九章:存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误
实战:第十九章:存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误
104 0
实战:第十九章:存入Long类型对象,在代码中使用Long类型接收,结果报类型转换错误
声明了Integer类型的两个数值并且值相等,但为什么返回了false?
声明了Integer类型的两个数值并且值相等,但为什么返回了false?
165 0
|
JavaScript
源生JS 之对象key值为数字时的取值及修改key值方法
源生JS 之对象key值为数字时的取值及修改key值方法
362 0
源生JS 之对象key值为数字时的取值及修改key值方法
算法将一个对象中的某一个key值变为true,其他值都为false
算法将一个对象中的某一个key值变为true,其他值都为false