From fc5e31062e9d128e95524a290f497c35f581e9db Mon Sep 17 00:00:00 2001 From: yanxutao89 <910135896@qq.com> Date: Thu, 24 Apr 2025 22:39:05 +0800 Subject: [PATCH] fix improve feature of NullAsDefaultValue, for issue #3518 --- .../com/alibaba/fastjson2/JSONWriter.java | 22 ++++++++ .../alibaba/fastjson2/JSONWriterUTF16.java | 2 +- .../com/alibaba/fastjson2/JSONWriterUTF8.java | 2 +- .../writer/FieldWriterBigDecimalField.java | 2 +- .../writer/FieldWriterBigDecimalFunc.java | 2 +- .../writer/FieldWriterBigDecimalMethod.java | 2 +- .../writer/FieldWriterBigIntField.java | 2 +- .../writer/FieldWriterBigIntFunc.java | 2 +- .../writer/FieldWriterDoubleFunc.java | 9 +-- .../writer/FieldWriterFloatFunc.java | 9 +-- .../fastjson2/writer/FieldWriterInt16.java | 2 +- .../fastjson2/writer/FieldWriterInt8.java | 2 +- .../fastjson2/writer/FieldWriterObject.java | 7 ++- .../writer/FieldWriterObjectFinal.java | 22 ++++---- .../writer/ObjectWriterCreatorASM.java | 29 ++++++---- .../alibaba/fastjson2/issues/Issue364.java | 2 +- .../fastjson2/issues_3500/Issue3515.java | 56 +++++++++++++++++++ .../issue_3400/Issue_20201016_01.java | 2 +- 18 files changed, 133 insertions(+), 43 deletions(-) create mode 100644 core/src/test/java/com/alibaba/fastjson2/issues_3500/Issue3515.java diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java index 942a846e6..92d0ea94d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriter.java @@ -1147,6 +1147,18 @@ public abstract class JSONWriter public abstract void writeNull(); + public void writeObjectNull(Class fieldClass) { + if ((this.context.features & (MASK_NULL_AS_DEFAULT_VALUE)) != 0) { + if (fieldClass == Character.class) { + writeString("\u0000"); + } else { + writeRaw('{', '}'); + } + } else { + writeNull(); + } + } + public void writeStringNull() { String raw; long features = this.context.features; @@ -1176,6 +1188,16 @@ public abstract class JSONWriter } } + public final void writeDecimalNull() { + if ((this.context.features & MASK_NULL_AS_DEFAULT_VALUE) != 0) { + writeDouble(0.0); + } else if ((this.context.features & MASK_WRITE_NULL_NUMBER_AS_ZERO) != 0) { + writeInt32(0); + } else { + writeNull(); + } + } + public final void writeInt64Null() { if ((this.context.features & (MASK_NULL_AS_DEFAULT_VALUE | MASK_WRITE_NULL_NUMBER_AS_ZERO)) != 0) { writeInt64(0); diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java index 94ca4946c..a8b96ebaa 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF16.java @@ -1045,7 +1045,7 @@ class JSONWriterUTF16 public final void writeDecimal(BigDecimal value, long features, DecimalFormat format) { if (value == null) { - writeNumberNull(); + writeDecimalNull(); return; } diff --git a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java index 0e91f1f52..0a2d70552 100644 --- a/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java +++ b/core/src/main/java/com/alibaba/fastjson2/JSONWriterUTF8.java @@ -2371,7 +2371,7 @@ final class JSONWriterUTF8 @Override public final void writeDecimal(BigDecimal value, long features, DecimalFormat format) { if (value == null) { - writeNumberNull(); + writeDecimalNull(); return; } diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalField.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalField.java index 4534b5e51..a8679be8a 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalField.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalField.java @@ -23,7 +23,7 @@ final class FieldWriterBigDecimalField BigDecimal value = (BigDecimal) getFieldValue(object); if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } } diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalFunc.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalFunc.java index 778c6923a..a3936a02f 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalFunc.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalFunc.java @@ -50,7 +50,7 @@ final class FieldWriterBigDecimalFunc if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } } diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalMethod.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalMethod.java index eae70c1ae..5831d272d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalMethod.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigDecimalMethod.java @@ -51,7 +51,7 @@ final class FieldWriterBigDecimalMethod if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } } diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntField.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntField.java index 95d789bca..fb6e5247e 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntField.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntField.java @@ -23,7 +23,7 @@ final class FieldWriterBigIntField BigInteger value = (BigInteger) getFieldValue(object); if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } } diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntFunc.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntFunc.java index 4fbe28cf8..50e5032f2 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntFunc.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterBigIntFunc.java @@ -41,7 +41,7 @@ final class FieldWriterBigIntFunc BigInteger value = function.apply(o); if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } } diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDoubleFunc.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDoubleFunc.java index 2d8955573..1e1acaf3f 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDoubleFunc.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterDoubleFunc.java @@ -58,11 +58,12 @@ final class FieldWriterDoubleFunc if (value == null) { long features = jsonWriter.getFeatures(this.features); - if ((features & JSONWriter.Feature.WriteNulls.mask) != 0 - && (features & JSONWriter.Feature.NotWriteDefaultValue.mask) == 0 - ) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { + return false; + } + if ((features & JSONWriter.Feature.NotWriteDefaultValue.mask) == 0) { writeFieldName(jsonWriter); - jsonWriter.writeNumberNull(); + jsonWriter.writeDecimalNull(); return true; } return false; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterFloatFunc.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterFloatFunc.java index 5196f2267..2de63a517 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterFloatFunc.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterFloatFunc.java @@ -58,11 +58,12 @@ final class FieldWriterFloatFunc if (value == null) { long features = jsonWriter.getFeatures(this.features); - if ((features & JSONWriter.Feature.WriteNulls.mask) != 0 - && (features & JSONWriter.Feature.NotWriteDefaultValue.mask) == 0 - ) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { + return false; + } + if ((features & JSONWriter.Feature.NotWriteDefaultValue.mask) == 0) { writeFieldName(jsonWriter); - jsonWriter.writeNumberNull(); + jsonWriter.writeDecimalNull(); return true; } return false; diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt16.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt16.java index fbce556a3..d9ef572d0 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt16.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt16.java @@ -46,7 +46,7 @@ abstract class FieldWriterInt16 if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } writeFieldName(jsonWriter); diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt8.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt8.java index 7fe96c0c5..5ef1af2d6 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt8.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterInt8.java @@ -45,7 +45,7 @@ abstract class FieldWriterInt8 if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } writeFieldName(jsonWriter); diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java index 3c181c02a..9817721d0 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java @@ -272,7 +272,10 @@ public class FieldWriterObject // (features & JSONWriter.Feature.WriteNullNumberAsZero.mask) != 0 if (value == null) { - if ((features & WriteNulls.mask) != 0 && (features & NotWriteDefaultValue.mask) == 0) { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { + return false; + } + if ((features & NotWriteDefaultValue.mask) == 0) { writeFieldName(jsonWriter); if (array) { jsonWriter.writeArrayNull(); @@ -283,7 +286,7 @@ public class FieldWriterObject || fieldClass == StringBuilder.class) { jsonWriter.writeStringNull(); } else { - jsonWriter.writeNull(); + jsonWriter.writeObjectNull(fieldClass); } return true; } else { diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObjectFinal.java b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObjectFinal.java index 136965d4c..1ddaa3926 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObjectFinal.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObjectFinal.java @@ -59,20 +59,18 @@ abstract class FieldWriterObjectFinal if (value == null) { long features = this.features | jsonWriter.getFeatures(); - if ((features & JSONWriter.Feature.WriteNulls.mask) != 0) { - writeFieldName(jsonWriter); - - if (fieldClass.isArray()) { - jsonWriter.writeArrayNull(); - } else if (fieldClass == StringBuffer.class || fieldClass == StringBuilder.class) { - jsonWriter.writeStringNull(); - } else { - jsonWriter.writeNull(); - } - return true; - } else { + if ((features & (JSONWriter.Feature.WriteNulls.mask | JSONWriter.Feature.NullAsDefaultValue.mask)) == 0) { return false; } + writeFieldName(jsonWriter); + if (fieldClass.isArray()) { + jsonWriter.writeArrayNull(); + } else if (fieldClass == StringBuffer.class || fieldClass == StringBuilder.class) { + jsonWriter.writeStringNull(); + } else { + jsonWriter.writeObjectNull(fieldClass); + } + return true; } ObjectWriter valueWriter = getObjectWriter(jsonWriter, fieldClass); diff --git a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java index 4457c5ec7..434c9aa5d 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java @@ -2526,7 +2526,7 @@ public class ObjectWriterCreatorASM // writeFieldName(w); gwFieldName(mwc, fieldWriter, i); - // jw.writeNulll + // jw.writeNull mw.aload(JSON_WRITER); mw.invokevirtual(TYPE_JSON_WRITER, "writeNull", "()V"); @@ -2813,6 +2813,8 @@ public class ObjectWriterCreatorASM } else if (fieldClass == String.class) { nullFeatures |= WriteNullStringAsEmpty.mask; nullFeatures |= NullAsDefaultValue.mask; + } else { + nullFeatures |= NullAsDefaultValue.mask; } mwc.genIsEnabled(nullFeatures, notNull_); // mw.iload(mwc.var(WRITE_NULLS)); @@ -2822,13 +2824,19 @@ public class ObjectWriterCreatorASM // writeFieldName(w); gwFieldName(mwc, fieldWriter, i); - // jw.writeNulll + // jw.writeNull + mw.aload(JSON_WRITER); String WRITE_NULL_METHOD; + String WRITE_NULL_DESC = "()V"; if (fieldClass == AtomicLongArray.class || fieldClass == AtomicIntegerArray.class || Collection.class.isAssignableFrom(fieldClass) || fieldClass.isArray()) { WRITE_NULL_METHOD = "writeArrayNull"; + } else if (fieldClass == Float.class + || fieldClass == Double.class + || fieldClass == BigDecimal.class) { + WRITE_NULL_METHOD = "writeDecimalNull"; } else if (Number.class.isAssignableFrom(fieldClass)) { WRITE_NULL_METHOD = "writeNumberNull"; } else if (fieldClass == Boolean.class) { @@ -2839,10 +2847,11 @@ public class ObjectWriterCreatorASM || fieldClass == StringBuilder.class) { WRITE_NULL_METHOD = "writeStringNull"; } else { - WRITE_NULL_METHOD = "writeNull"; + WRITE_NULL_METHOD = "writeObjectNull"; + WRITE_NULL_DESC = "(Ljava/lang/Class;)V"; + mwc.loadFieldClass(i, fieldClass); } - mw.aload(JSON_WRITER); - mw.invokevirtual(TYPE_JSON_WRITER, WRITE_NULL_METHOD, "()V"); + mw.invokevirtual(TYPE_JSON_WRITER, WRITE_NULL_METHOD, WRITE_NULL_DESC); mw.visitLabel(notNull_); } @@ -3005,7 +3014,7 @@ public class ObjectWriterCreatorASM // writeFieldName(w); gwFieldName(mwc, fieldWriter, i); - // jw.writeNulll + // jw.writeNull mw.aload(JSON_WRITER); mw.invokevirtual(TYPE_JSON_WRITER, "writeArrayNull", "()V"); @@ -3239,7 +3248,7 @@ public class ObjectWriterCreatorASM gwFieldName(mwc, fieldWriter, i); mw.aload(JSON_WRITER); - mw.invokevirtual(TYPE_JSON_WRITER, "writeNumberNull", "()V"); + mw.invokevirtual(TYPE_JSON_WRITER, "writeDecimalNull", "()V"); mw.goto_(endIfNull_); @@ -3300,7 +3309,7 @@ public class ObjectWriterCreatorASM gwFieldName(mwc, fieldWriter, i); mw.aload(JSON_WRITER); - mw.invokevirtual(TYPE_JSON_WRITER, "writeNumberNull", "()V"); + mw.invokevirtual(TYPE_JSON_WRITER, "writeDecimalNull", "()V"); mw.goto_(endIfNull_); @@ -3484,7 +3493,7 @@ public class ObjectWriterCreatorASM mw.visitLabel(writeNull_); gwFieldName(mwc, fieldWriter, i); - // jw.writeNulll + // jw.writeNull mw.aload(JSON_WRITER); mw.invokevirtual(TYPE_JSON_WRITER, "writeNull", "()V"); @@ -4190,7 +4199,7 @@ public class ObjectWriterCreatorASM mw.invokevirtual(TYPE_JSON_WRITER, "writeString", "(Ljava/lang/String;)V"); mw.goto_(endIfNull_); - // jw.writeNulll + // jw.writeNull mw.visitLabel(writeNullValue_); mw.aload(JSON_WRITER); mw.invokevirtual(TYPE_JSON_WRITER, "writeStringNull", "()V"); diff --git a/core/src/test/java/com/alibaba/fastjson2/issues/Issue364.java b/core/src/test/java/com/alibaba/fastjson2/issues/Issue364.java index 9582d2885..6cda849b4 100644 --- a/core/src/test/java/com/alibaba/fastjson2/issues/Issue364.java +++ b/core/src/test/java/com/alibaba/fastjson2/issues/Issue364.java @@ -11,7 +11,7 @@ public class Issue364 { @Test public void test() { TestBean bean = new TestBean(); - assertEquals("{\"msg\":\"\"}", JSON.toJSONString(bean, JSONWriter.Feature.NullAsDefaultValue)); + assertEquals("{\"bean\":{},\"msg\":\"\"}", JSON.toJSONString(bean, JSONWriter.Feature.NullAsDefaultValue)); } @Data diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_3500/Issue3515.java b/core/src/test/java/com/alibaba/fastjson2/issues_3500/Issue3515.java new file mode 100644 index 000000000..9d440e73e --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_3500/Issue3515.java @@ -0,0 +1,56 @@ +package com.alibaba.fastjson2.issues_3500; + +import com.alibaba.fastjson2.JSON; +import com.alibaba.fastjson2.JSONWriter; +import lombok.Getter; +import lombok.Setter; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class Issue3515 { + @Test + public void test() throws Exception { + String jsonString = JSON.toJSONString(new Bean(), JSONWriter.Feature.NullAsDefaultValue); + Bean bean = JSON.parseObject(jsonString, Bean.class); + assertFalse(bean.getBoolVal()); + assertEquals((byte) 0, bean.getByteVal()); + assertEquals('\u0000', bean.getCharVal()); + assertEquals((short) 0, bean.getShortVal()); + assertEquals(0, bean.getIntVal()); + assertEquals(0L, bean.getLongVal()); + assertEquals(0.0F, bean.getFloatVal()); + assertEquals(0.0D, bean.getDoubleVal()); + assertEquals("", bean.getStringVal()); + assertEquals("{}", bean.getMapVal().toString()); + assertEquals("[]", bean.getListVal().toString()); + assertEquals(BigInteger.ZERO, bean.getIntegerVal()); + assertEquals(new BigDecimal("0.0"), bean.getDecimalVal()); + assertEquals("{}", bean.getObjectVal().toString()); + } + + @Getter + @Setter + static class Bean { + private Boolean boolVal; + private Byte byteVal; + private Character charVal; + private Short shortVal; + private Integer intVal; + private Long longVal; + private Float floatVal; + private Double doubleVal; + private String stringVal; + private Map mapVal; + private List listVal; + private BigDecimal decimalVal; + private BigInteger integerVal; + private Object objectVal; + } +} diff --git a/core/src/test/java/com/alibaba/fastjson2/v1issues/issue_3400/Issue_20201016_01.java b/core/src/test/java/com/alibaba/fastjson2/v1issues/issue_3400/Issue_20201016_01.java index e8c500390..92111e7df 100644 --- a/core/src/test/java/com/alibaba/fastjson2/v1issues/issue_3400/Issue_20201016_01.java +++ b/core/src/test/java/com/alibaba/fastjson2/v1issues/issue_3400/Issue_20201016_01.java @@ -19,7 +19,7 @@ public class Issue_20201016_01 { String s = JSON.toJSONString(config, JSONWriter.Feature.WriteNulls, JSONWriter.Feature.NullAsDefaultValue); - assertEquals("{\"agent\":null,\"creator\":{\"account\":\"account\",\"name\":\"name\",\"workid\":\"\"},\"owner\":{\"account\":\"account\",\"name\":\"name\",\"workid\":\"\"}}", s); + assertEquals("{\"agent\":{},\"creator\":{\"account\":\"account\",\"name\":\"name\",\"workid\":\"\"},\"owner\":{\"account\":\"account\",\"name\":\"name\",\"workid\":\"\"}}", s); } @Test