From 0024891c8ed495d6463bbcb52419cf0fa2101c2d Mon Sep 17 00:00:00 2001 From: "shaojin.wensj" Date: Fri, 9 Feb 2024 09:20:08 +0800 Subject: [PATCH] fix jsonb WriteNulls, for issue #2234 --- .../writer/ObjectWriterCreatorASM.java | 91 ++++++++++++++++--- .../java/com/alibaba/fastjson2/ListTest.java | 8 +- .../autoType/AutoTypeTest37_MapBean.java | 2 +- .../autoType/AutoTypeTest41_dupRef.java | 10 +- .../alibaba/fastjson2/dubbo/DubboTest6.java | 25 ++--- .../fastjson2/issues_2200/Issue2234.java | 34 +++++++ 6 files changed, 142 insertions(+), 28 deletions(-) create mode 100644 core/src/test/java/com/alibaba/fastjson2/issues_2200/Issue2234.java 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 5fb12db695..a0013f8e77 100644 --- a/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java +++ b/core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java @@ -2492,12 +2492,11 @@ private void gwFieldValueObjectJSONB( int OBJECT, int i ) { - MethodWriter mw = mwc.mw; Class fieldClass = fieldWriter.fieldClass; + Type fieldType = fieldWriter.fieldType; String fieldName = fieldWriter.fieldName; boolean refDetection = !ObjectWriterProvider.isNotReferenceDetect(fieldClass); - int FIELD_VALUE = mwc.var(fieldClass); Integer REF_PATH = null; @@ -2505,13 +2504,16 @@ private void gwFieldValueObjectJSONB( REF_PATH = mwc.var("REF_PATH"); } - Label endIfNull_ = new Label(), notNull_ = new Label(); + long features = fieldWriter.features | mwc.objectFeatures; + MethodWriter mw = mwc.mw; + + Label null_ = new Label(), notNull_ = new Label(); genGetObject(mwc, fieldWriter, i, OBJECT); mw.visitInsn(Opcodes.DUP); mw.visitVarInsn(Opcodes.ASTORE, FIELD_VALUE); - mw.visitJumpInsn(Opcodes.IFNULL, endIfNull_); + mw.visitJumpInsn(Opcodes.IFNULL, null_); if (!Serializable.class.isAssignableFrom(fieldClass) && fieldClass != List.class) { mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER); @@ -2521,7 +2523,7 @@ private void gwFieldValueObjectJSONB( mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE); mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "isIgnoreNoneSerializable", "(Ljava/lang/Object;)Z", false); } - mw.visitJumpInsn(Opcodes.IFNE, endIfNull_); + mw.visitJumpInsn(Opcodes.IFNE, null_); } /** @@ -2530,7 +2532,7 @@ private void gwFieldValueObjectJSONB( * if (value == object) { * writeFieldName(jsonWriter); * jsonWriter.writeReference(".."); - * goto endIfNull_ + * goto null_ * } * * String refPath = context.setPath(name, value); @@ -2538,7 +2540,7 @@ private void gwFieldValueObjectJSONB( * writeFieldName(jsonWriter); * jsonWriter.writeReference(refPath); * context.popPath(); - * goto endIfNull_ + * goto null_ * } * } */ @@ -2569,7 +2571,7 @@ private void gwFieldValueObjectJSONB( mw.visitLdcInsn(".."); mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "writeReference", "(Ljava/lang/String;)V", false); - mw.visitJumpInsn(Opcodes.GOTO, endIfNull_); + mw.visitJumpInsn(Opcodes.GOTO, null_); mw.visitLabel(refSetPath_); @@ -2591,16 +2593,30 @@ private void gwFieldValueObjectJSONB( mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER); mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE); mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "popPath", "(Ljava/lang/Object;)V", false); - mw.visitJumpInsn(Opcodes.GOTO, endIfNull_); + mw.visitJumpInsn(Opcodes.GOTO, null_); mw.visitLabel(endDetect_); if ("this$0".equals(fieldName) || "this$1".equals(fieldName) || "this$2".equals(fieldName)) { mw.visitVarInsn(Opcodes.ILOAD, REF_DETECT); - mw.visitJumpInsn(Opcodes.IFEQ, endIfNull_); + mw.visitJumpInsn(Opcodes.IFEQ, null_); } } + if (Object[].class.isAssignableFrom(fieldClass)) { + Label notWriteEmptyArrayEnd_ = new Label(); + mwc.genIsEnabled(JSONWriter.Feature.NotWriteEmptyArray.mask, notWriteEmptyArrayEnd_); + + mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE); + mw.visitTypeInsn(Opcodes.CHECKCAST, "[Ljava/lang/Object;"); + mw.visitInsn(Opcodes.ARRAYLENGTH); + mw.visitJumpInsn(Opcodes.IFNE, notWriteEmptyArrayEnd_); + + mw.visitJumpInsn(Opcodes.GOTO, notNull_); + + mw.visitLabel(notWriteEmptyArrayEnd_); + } + gwFieldName(mwc, fieldWriter, i); // fw.getObjectWriter(w, value.getClass()); @@ -2644,7 +2660,60 @@ private void gwFieldValueObjectJSONB( mw.visitLabel(endDetect_); } - mw.visitLabel(endIfNull_); + mw.visitJumpInsn(Opcodes.GOTO, notNull_); + + mw.visitLabel(null_); + + // if (!jw.isWriteNulls()) + if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) { + long nullFeatures = JSONWriter.Feature.WriteNulls.mask; + if (fieldClass == AtomicLongArray.class + || fieldClass == AtomicIntegerArray.class + || Collection.class.isAssignableFrom(fieldClass) + || fieldClass.isArray()) { + nullFeatures |= WriteNullListAsEmpty.mask; + nullFeatures |= NullAsDefaultValue.mask; + } else if (Number.class.isAssignableFrom(fieldClass)) { + nullFeatures |= WriteNullNumberAsZero.mask; + nullFeatures |= NullAsDefaultValue.mask; + } else if (fieldClass == Boolean.class) { + nullFeatures |= WriteNullBooleanAsFalse.mask; + nullFeatures |= NullAsDefaultValue.mask; + } else if (fieldClass == String.class) { + nullFeatures |= WriteNullStringAsEmpty.mask; + nullFeatures |= NullAsDefaultValue.mask; + } + mwc.genIsEnabled(nullFeatures, notNull_); +// mw.visitVarInsn(Opcodes.ILOAD, mwc.var(WRITE_NULLS)); +// mw.visitJumpInsn(Opcodes.IFEQ, notNull_); + } + + // writeFieldName(w); + gwFieldName(mwc, fieldWriter, i); + + // jw.writeNulll + String WRITE_NULL_METHOD; + if (fieldClass == AtomicLongArray.class + || fieldClass == AtomicIntegerArray.class + || Collection.class.isAssignableFrom(fieldClass) + || fieldClass.isArray()) { + WRITE_NULL_METHOD = "writeArrayNull"; + } else if (Number.class.isAssignableFrom(fieldClass)) { + WRITE_NULL_METHOD = "writeNumberNull"; + } else if (fieldClass == Boolean.class) { + WRITE_NULL_METHOD = "writeBooleanNull"; + } else if (fieldClass == String.class + || fieldClass == Appendable.class + || fieldClass == StringBuffer.class + || fieldClass == StringBuilder.class) { + WRITE_NULL_METHOD = "writeStringNull"; + } else { + WRITE_NULL_METHOD = "writeNull"; + } + mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER); + mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, WRITE_NULL_METHOD, "()V", false); + + mw.visitLabel(notNull_); } private static void gwListSimpleType( diff --git a/core/src/test/java/com/alibaba/fastjson2/ListTest.java b/core/src/test/java/com/alibaba/fastjson2/ListTest.java index 2287f9b4e3..e5cfd01add 100644 --- a/core/src/test/java/com/alibaba/fastjson2/ListTest.java +++ b/core/src/test/java/com/alibaba/fastjson2/ListTest.java @@ -15,7 +15,9 @@ public void testNull() { assertEquals(expected, new String(JSON.toJSONBytes(bean, JSONWriter.Feature.WriteNulls))); assertEquals(expected, JSON.toJSONString(bean, JSONWriter.Feature.WriteNulls)); byte[] jsonbBytes = JSONB.toBytes(bean, JSONWriter.Feature.WriteNulls); - assertEquals("{}", JSONB.toJSONString(jsonbBytes, true)); + assertEquals("{\n" + + "\t\"list\":null\n" + + "}", JSONB.toJSONString(jsonbBytes, true)); } @Test @@ -50,7 +52,9 @@ public void testNull1() { assertEquals(expected, new String(JSON.toJSONBytes(bean, JSONWriter.Feature.WriteNulls))); assertEquals(expected, JSON.toJSONString(bean, JSONWriter.Feature.WriteNulls)); byte[] jsonbBytes = JSONB.toBytes(bean, JSONWriter.Feature.WriteNulls); - assertEquals("{}", JSONB.toJSONString(jsonbBytes, true)); + assertEquals("{\n" + + "\t\"list\":null\n" + + "}", JSONB.toJSONString(jsonbBytes, true)); } @Test diff --git a/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest37_MapBean.java b/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest37_MapBean.java index d2ff890d2d..c41ddfb078 100644 --- a/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest37_MapBean.java +++ b/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest37_MapBean.java @@ -37,7 +37,7 @@ public void test_1() throws Exception { JSONB.dump(bytes); - assertEquals(144, bytes.length); + assertEquals(186, bytes.length); // 142 200 202 216 141 151 144 Bean bean2 = (Bean) JSONB.parseObject( diff --git a/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest41_dupRef.java b/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest41_dupRef.java index fa04c96773..16c233826a 100644 --- a/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest41_dupRef.java +++ b/core/src/test/java/com/alibaba/fastjson2/autoType/AutoTypeTest41_dupRef.java @@ -38,8 +38,11 @@ public void test_1() throws Exception { "\t\t\t\"id#2\":1001\n" + "\t\t},\n" + "\t\t\"item1#3\":{\"$ref\":\"$.item0\"},\n" + + "\t\t\"#3\":null,\n" + "\t\t\"item2#4\":{\"$ref\":\"#-1\"},\n" + - "\t\t\"item3#5\":{\"$ref\":\"#-1\"}\n" + + "\t\t\"#4\":null,\n" + + "\t\t\"item3#5\":{\"$ref\":\"#-1\"},\n" + + "\t\t\"#5\":null\n" + "\t}\n" + "}", JSONB.toJSONString(bytes, true)); @@ -49,8 +52,11 @@ public void test_1() throws Exception { "\t\t\"id\":1001\n" + "\t},\n" + "\t\"item1\":{\"$ref\":\"$.item0\"},\n" + + "\t\"item1\":null,\n" + "\t\"item2\":{\"$ref\":\"#-1\"},\n" + - "\t\"item3\":{\"$ref\":\"#-1\"}\n" + + "\t\"item2\":null,\n" + + "\t\"item3\":{\"$ref\":\"#-1\"},\n" + + "\t\"item3\":null\n" + "}", JSONB.toJSONString(bytes, false)); Bean bean2 = (Bean) JSONB.parseObject( diff --git a/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest6.java b/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest6.java index 15d6698f65..63a3345087 100644 --- a/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest6.java +++ b/core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest6.java @@ -53,18 +53,19 @@ public void test1() { byte[] jsonbBytes = JSONB.toBytes(proxy, writerFeatures); assertEquals("{\n" + - "\t\"@type\":\"org.apache.dubbo.springboot.demo.ParamsDTO#0\",\n" + - "\t\"@value\":{\n" + - "\t\t\"paramsItems#1\":{\n" + - "\t\t\t\"@type\":\"java.util.Arrays$ArrayList#2\",\n" + - "\t\t\t\"@value\":[\n" + - "\t\t\t\t{\n" + - "\t\t\t\t\t\"a#3\":\"aaa\"\n" + - "\t\t\t\t}\n" + - "\t\t\t]\n" + - "\t\t}\n" + - "\t}\n" + - "}", + "\t\"@type\":\"org.apache.dubbo.springboot.demo.ParamsDTO#0\",\n" + + "\t\"@value\":{\n" + + "\t\t\"paramsItemSet#1\":null,\n" + + "\t\t\"paramsItems#2\":{\n" + + "\t\t\t\"@type\":\"java.util.Arrays$ArrayList#3\",\n" + + "\t\t\t\"@value\":[\n" + + "\t\t\t\t{\n" + + "\t\t\t\t\t\"a#4\":\"aaa\"\n" + + "\t\t\t\t}\n" + + "\t\t\t]\n" + + "\t\t}\n" + + "\t}\n" + + "}", JSONB.toJSONString(jsonbBytes, true) ); diff --git a/core/src/test/java/com/alibaba/fastjson2/issues_2200/Issue2234.java b/core/src/test/java/com/alibaba/fastjson2/issues_2200/Issue2234.java new file mode 100644 index 0000000000..a445338f55 --- /dev/null +++ b/core/src/test/java/com/alibaba/fastjson2/issues_2200/Issue2234.java @@ -0,0 +1,34 @@ +package com.alibaba.fastjson2.issues_2200; + +import com.alibaba.fastjson2.JSONB; +import com.alibaba.fastjson2.JSONWriter; +import org.junit.jupiter.api.Test; + +public class Issue2234 { + @Test + public void test() { + Bean bean = new Bean(); + bean.f0 = null; + bean.f1 = null; + bean.f2 = null; + bean.f3 = null; + bean.f4 = null; + bean.f5 = null; + bean.f6 = null; + bean.f7 = null; + + byte[] jsonb = JSONB.toBytes(bean, JSONWriter.Feature.WriteNulls, JSONWriter.Feature.FieldBased); + System.out.println(JSONB.toJSONString(jsonb)); + } + + static class Bean { + private Boolean f0 = true; + private Byte f1 = 101; + private Short f2 = 101; + private Integer f3 = 102; + private Long f4 = 103L; + private Float f5 = 103F; + private Double f6 = 103d; + private Character f7 = 'a'; + } +}