[alibaba/fastjson]@JSONField(serializeUsing = XXX.class)自定义Serializer bug

2024-09-20 921 views
9

public class Test { private String name; @JSONField(serializeUsing = ToStringSerializer.class) private Integer age; public static void main(String[] args) { Test test = new Test(); test.setName("张三"); test.setAge(null); System.out.println(JSON.toJSONString(test)); } // get set }

上面不会进入ToStringSerializer.writer 方法。

//这里不会执行 if (object == null) { out.writeNull(); }

回答

3

版本1.2.76也不行

8

请问对于

public class Test {
  private String name;
  @JSONField(serializeUsing = ToStringSerializer.class)
  private Integer age;
  public static void main(String[] args) {
    Test test = new Test();
    test.setName("张三");
    test.setAge(null);
    System.out.println(JSON.toJSONString(test));
  }
// get set
}

你想要的预期效果是这样的吗: {"age":"null","name":"张三"}

4

@Tloops 我的预期是当age 为null 时,能进入ToStringSerializer里面的write方法。 因为方法里面有一段代码 if (object == null) { out.writeNull(); } 但是现在这种null情况不会进入这个序列化方法,所以,上面这段代码是不会生效的。(ToStringSerializer是fastjson自带的,其他自定义序列化方法也是不会有null情况进入。)所以我觉得这是一个bug

3

从源码跟了一下,现在的代码,对于null值处理时,针对Number、String、Boolean、Collection这四种类型是不支持JSONField.serializeUsing 的(这里是做特例处理造成的,不知道是否有什么考量)

有两段相关代码 1.JavaBeanSerializer中,对于属性值为null处理时,会特殊处理几个特定类型,对于这里相关的Number.class类型处理时,会优先考虑定义的特性(SerializerFeature),如果null不写则直接跳过处理,所以也就不会再进注解自定义的处理器,相关代码为 JavaBeanSerializer 350行 } else if (Number.class.isAssignableFrom(fieldClass)) { int defaultMask = SerializerFeature.WriteNullNumberAsZero.mask; final int mask = defaultMask | SerializerFeature.WriteMapNullValue.mask; if ((!writeAsArray) && (serialzeFeatures & mask) == 0 && (out.features & mask) == 0) { continue; }

2.如果这里我们开启WriteMapNullValue,执行了FieldSerializer(实现JSONField处理的地方)了,则会因为下述代码直写null而不触发JSONField.serializeUsing 的处理逻辑

//FieldSerializer 233行 if (Number.class.isAssignableFrom(runtimeFieldClass)) { out.writeNull(features, SerializerFeature.WriteNullNumberAsZero.mask); return; } else if (String.class == runtimeFieldClass) { out.writeNull(features, SerializerFeature.WriteNullStringAsEmpty.mask); return; } else if (Boolean.class == runtimeFieldClass) { out.writeNull(features, SerializerFeature.WriteNullBooleanAsFalse.mask); return; } else if (Collection.class.isAssignableFrom(runtimeFieldClass) || runtimeFieldClass.isArray()) { out.writeNull(features, SerializerFeature.WriteNullListAsEmpty.mask); return; }

3

从源码跟了一下,现在的代码,对于null值处理时,针对Number、String、Boolean、Collection这四种类型是不支持JSONField.serializeUsing 的(这里是做特例处理造成的,不知道是否有什么考量)

有两段相关代码 1.JavaBeanSerializer中,对于属性值为null处理时,会特殊处理几个特定类型,对于这里相关的Number.class类型处理时,会优先考虑定义的特性(SerializerFeature),如果null不写则直接跳过处理,所以也就不会再进注解自定义的处理器,相关代码为 JavaBeanSerializer 350行 } else if (Number.class.isAssignableFrom(fieldClass)) { int defaultMask = SerializerFeature.WriteNullNumberAsZero.mask; final int mask = defaultMask | SerializerFeature.WriteMapNullValue.mask; if ((!writeAsArray) && (serialzeFeatures & mask) == 0 && (out.features & mask) == 0) { continue; }

2.如果这里我们开启WriteMapNullValue,执行了FieldSerializer(实现JSONField处理的地方)了,则会因为下述代码直写null而不触发JSONField.serializeUsing 的处理逻辑

//FieldSerializer 233行 if (Number.class.isAssignableFrom(runtimeFieldClass)) { out.writeNull(features, SerializerFeature.WriteNullNumberAsZero.mask); return; } else if (String.class == runtimeFieldClass) { out.writeNull(features, SerializerFeature.WriteNullStringAsEmpty.mask); return; } else if (Boolean.class == runtimeFieldClass) { out.writeNull(features, SerializerFeature.WriteNullBooleanAsFalse.mask); return; } else if (Collection.class.isAssignableFrom(runtimeFieldClass) || runtimeFieldClass.isArray()) { out.writeNull(features, SerializerFeature.WriteNullListAsEmpty.mask); return; }

看源码下来有一个体验就是... 现在的实现里特例处理的情景太多了,而且没有注释说明,不知道因为什么考量引入的特例处理... ... 这就导致了能看清楚引发问题的逻辑是什么... ... 但是不敢改..

2

回归到这个问题的解决办法,个人觉得有一个方案是 1.业务上设定 0、null、空值同一个业务含义 2.然后就是转不转出来都没关系了.... 需要转出来null的话,用 JSON.toJSONString(test, SerializerFeature.WriteMapNullValue) 这种写法即可

7

@RLin2015New 仔细看了你的解析,非常感谢你的回答。我原先的需求是。对于Number类型的字段。在serializeUsing时,可以生成一个新的字段(比如BigDecimal amount 生成String amountStr = "5")。用于前端展示,解决浮点数精度丢失问题。后面发现这个null时不进入,也就不能适配null的情况。所以也就用其他方法代替。