json,你真的足够了解吗?
json作为独立于语言的轻量级数据交换格式,在如今互联网开发中数据交互中有着非凡对意义。如果是基于Restful风格开发的程序,基本上都使用json进行端与端的数据通信。从我入行到今天,不知不觉已经和它打了将多年交道。可以说接触json的机会数不胜数。殊不知,原来它是我最熟悉的陌生人。
背景
先看一段代码
public class PushSink extends RichSinkFunction<Row> {
private PushEmitter emitter;
public void open(Configuration parameters) {
emitter = new PushEmitter();
}
public void close() {
}
/**
* 每条数据的插入都要调用一次 invoke() 方法
*
* @param value Kafka json消息
* @throws Exception 异常
*/
public void invoke(Row value, Context context) throws Exception {
String gifts = value.getField(0).toString();
log.debug("row: {}, str: {}", value, gifts);
if(!JSON.isValid(gifts)) {
return;
}
if(JSON.isValidArray(gifts)){
emitter.push(JSON.parseObject(gifts, new TypeReference<List<Gift>>(){}));
} else {
Gift gift = JSON.parseObject(gifts, Gift.class);
emitter.push(ImmutableList.of(gift));
}
}
}
该段代码的业务背景是在使用FlinkKafkaConsumer对指定topic进行实时流消费,Kafka topic 中的数据为json字符串。这是一段简单的flink sink 对经过etl后的流数据进行真实处理业务处理前异常校验的代码,本来该段代码的本意是验证输入数据是否为标准的json数据,防止最终落库的时候写入脏数据。在对该段代码进行实现逻辑测试的时候,乍一看没有啥问题,写的也很简洁。但是由于平常很少用JSON.isValid()
这个方法,所以上“一查你就GG的-百度”查了一下它的用法。这不查不知道,一查真的吓一跳。
‘坑’
对于JSON.isValid(gifts)
,是com.alibaba.fastjson.JSON类下的一个静态方法。网络上有很多人对其进行吐槽,甚至于被爆有严重bug
“严重bug”
还有跟帖
实际测试结果
测试时候版本已经升级到1.2.70,也就是目前到最新版本。看起来阿里巴巴并没有及时修复该“严重BUG”。
我把此事及时告知了负责写该块业务逻辑的阿G同学,G同学的反应也是
显然,如果有这种问题,那么在该业务场景下的校验可能失效。无法剥离原意中非标准json(jsonObject)的脏数据。那么可能会有部分数据例如:3.14,123456 等数值型的数据进入下游环节,导致最终落盘数据有误。
之后大概5分钟,我和阿G陷入了对fastjson的吐槽中。。。
原因
不过转过头来想,为啥结果会是这样的???
阿G是个实干派,说改就改。但是要怎么改?于是去翻阅了fastjson的源码。我们发现在1.2.70版本的JSONValidator抽象类中,有个内部枚举类Type。显示了json的三种结构类型
public static enum Type {
Object,
Array,
Value;
private Type() {
}
}
等一下,Value是个什么玩意?Object和Array我们都知道,也是用的最经常的两种类型。甚至在此之前,可能很多人以为json就只有这两种类型(Object和Array)。为了弄清楚这个问题,我又查了json的官方 文档。文档中明确提及到json的Value数据类型json官方链接[1]
所以看起来也许不可思议。像3.14,123456这样的单个数值也是可以通过json校验的,是合法的json的格式。那么像之前测试代码中的字符串呢?文档中也告诉我们字符串是需要进行转义才可以通过校验。
A string is a sequence of zero or more Unicode characters, wrapped in double quotes, using backslash escapes. A character is represented as a single character string. A string is very much like a C or Java string.
所以修改后的测试代码是
System.out.println(JSON.isValid("123456"));
System.out.println(JSON.isValid("\"hello json\""));
这样测试出来的结果才是正常的json
尾声
即便现在对json有了更为深入的了解,但是显然在此前的业务场景中,使用JSON.isValid()方法无法有效保证所有的非jsonObejct 数据被过滤。其中一种可能的解决方法是像阿G后面改成的这样
JSONValidator validator = JSONValidator.from(json);
if (!validator.validate()) {
return;
}
if (validator.getType() == JSONValidator.Type.Array) {
....
}));
} else if (validator.getType() == JSONValidator.Type.Object) {
....
}
具体的校验方法还是要根据实际的业务场景进行制定。在此次问题发现、解决的过程中,弥补了不少对json的知识点。但是对我而言,精神上的收获远比知识提升来得愉悦。在实际工作中,对于不熟悉的事物,一定要有刨根到底的研究精神,才能把未知的隐藏bug消灭于底层之中。这大概就是代码走读所带给我的最大的快乐。
References
[1]
json官方链接: https://www.json.org/json-en.html