?一、?Date类型字段不支持输出为空字段
问题描述
fastjson2.0中Date类型字段设置JSONWriter.Feature.WriteMapNullValue 不生效
重现步骤
public class Test {
@com.alibaba.fastjson.annotation.JSONField(serialzeFeatures = { SerializerFeature.WriteMapNullValue })
@com.alibaba.fastjson2.annotation.JSONField(serializeFeatures = {
JSONWriter.Feature.WriteMapNullValue }, format = "millis")
private Date updateTime;
/**
* @return the updateTime
*/
public Date getUpdateTime() {
return updateTime;
}
/**
* @param updateTime the updateTime to set
*/
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public static void main(String[] args) {
Test a = new Test();
System.out.println(com.alibaba.fastjson.JSON.toJSONString(a));
System.out.println(com.alibaba.fastjson2.JSON.toJSONString(a));
}
}
结果:
{"updateTime":null}
{}
期望结果:
{"updateTime":null}
{"updateTime":null}
根据给出的代码和期望结果,可以看出问题是在对null值进行序列化时,没有正确地设置WriteMapNullValue特性。在fastjson的源代码中,可以找到对null值的序列化逻辑。根据给出的结果,可以看出在序列化时,如果字段值为null,则不会包含该字段。这与期望结果不符。
我们分析一下给出修改的地方它修改的类ObjectWriterCreatorASMcom
这个类实现了动态创建对象写入器(ObjectWriter)的功能。它使用 ASM(Java 字节码操作框架)来生成字节码,以在运行时动态创建对象写入器。这些对象写入器用于将 Java 对象序列化为 JSON 格式的字符串。
源代码:
mw.visitVarInsn(Opcodes.ILOAD, mwc.var(WRITE_NULLS));
mw.visitJumpInsn(Opcodes.IFNE, writeNull_);
mw.visitJumpInsn(Opcodes.GOTO, endIfNull_);
修改后的代码:
if ((fieldWriter.features & WriteNulls.mask) == 0) {
mw.visitVarInsn(Opcodes.ILOAD, mwc.var(WRITE_NULLS));
mw.visitJumpInsn(Opcodes.IFNE, writeNull_);
mw.visitJumpInsn(Opcodes.GOTO, endIfNull_); }
在原始代码中,通过mw.visitVarInsn(Opcodes.ILOAD, mwc.var(WRITE_NULLS))
加载WRITE_NULLS
变量的值,然后通过mw.visitJumpInsn(Opcodes.IFNE, writeNull_)
判断是否需要写入null值。但是,在特定情况下,这种判断可能会出现问题,导致设置JSONWriter.Feature.WriteMapNullValue
无法生效。
而修改后的代码添加了一个条件判断:首先对fieldWriter.features
与WriteNulls.mask
进行按位与操作,判断是否为0。如果结果为0,说明WriteNulls
特性未被设置,那么就不需要写入null值,直接跳过写入null值的逻辑。
二、BigDecimal 0e-7序列化时候会有bug
问题描述
调用 JSONObject.toJSONString(jsonObject); 当对象中有bigdecimal = 0e-7 字段时候,IOUtil 在 writeDecimal时候如果off = buffer.size 不会进行扩容,会报数组下标越界
?
java.lang.ArrayIndexOutOfBoundsException: Index 314928 out of bounds for length 314928
at com.alibaba.fastjson2.util.IOUtils.writeInt64(IOUtils.java:1085)
at com.alibaba.fastjson2.util.IOUtils.writeDecimal(IOUtils.java:381)
at com.alibaba.fastjson2.JSONWriterUTF16.writeDecimal(JSONWriterUTF16.java:1305)
at com.alibaba.fastjson2.writer.ObjectWriterImplMap.write(ObjectWriterImplMap.java:497)
at com.alibaba.fastjson2.writer.ObjectWriterImplList.write(ObjectWriterImplList.java:366)
at com.alibaba.fastjson2.writer.ObjectWriterImplMap.write(ObjectWriterImplMap.java:548)
at com.alibaba.fastjson2.writer.ObjectWriterImplList.write(ObjectWriterImplList.java:366)
at com.alibaba.fastjson2.writer.ObjectWriterImplMap.write(ObjectWriterImplMap.java:548)
at com.alibaba.fastjson2.JSON.toJSONString(JSON.java:2815)
at com.alibaba.fastjson2.JSONObject.toJSONString(JSONObject.java:1144)
at com.sch.order.application.service.impl.SplitOrderServiceImpl.main(SplitOrderServiceImpl.java:412)
重现步骤
public static void main(String[] args) throws IOException {
String str = String.valueOf(FileUtil.readChars(new File("C:\***\SO1231205000018.txt")));
JSONObject.toJSONString(str);
JSONObject jsonObject = JSONObject.parseObject(str);
JSONObject.toJSONString(jsonObject);
}
这个问题存在的原因是当对 BigDecimal 进行序列化时,可能会出现精度丢失或产生不正确的值的问题。这是由于默认的序列化机制将 BigDecimal 对象视为一个标准的浮点数进行处理,而浮点数在表示精确小数时存在精度限制。
让我们看看fastjson2里更新版本是怎么解决这个问题的吧,有没有更有启发性的解决方案。
它将原来的代码int minCapacity = off + precision + 7;修改为了int minCapacity = off + precision + value.scale() + 7;多了一个value.scale(),首先介绍一下用于计算变量 minCapacity(
序列化过程中所需的最小容量)
的值中变量的含义
off
?是一个偏移量,表示在字符串中的起始位置。precision
?是一个整数,表示要序列化的数字的精度。value.scale()
?返回一个整数,表示要序列化的数字的小数位数。7
?是一个常数,表示额外的字符(包括小数点、负号等)占用的字符数。通过将以上这些值相加,可以得到序列化过程中所需的最小容量。这个容量是为了确保字符串缓冲区具有足够的空间来存储序列化后的数字。
value.scale()
它是序列化的数字的小数位数,加上他可以准确地反映数字的小数位数。这样计算出的 minCapacity
?才不会低估所需的最小容量。可以使字符串缓冲区有足够的的空间去存储序列化后的数字,从而避免了数据截断或溢出的问题。也能成功的解决BigDecimal 0e-7数组溢出的问题。