Skip to content

Commit

Permalink
fix jsonb WriteNulls, for issue #2234
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Feb 9, 2024
1 parent f71cd4b commit 0024891
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2492,26 +2492,28 @@ 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;
if (refDetection) {
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);
Expand All @@ -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_);
}

/**
Expand All @@ -2530,15 +2532,15 @@ private void gwFieldValueObjectJSONB(
* if (value == object) {
* writeFieldName(jsonWriter);
* jsonWriter.writeReference("..");
* goto endIfNull_
* goto null_
* }
*
* String refPath = context.setPath(name, value);
* if (refPath != null) {
* writeFieldName(jsonWriter);
* jsonWriter.writeReference(refPath);
* context.popPath();
* goto endIfNull_
* goto null_
* }
* }
*/
Expand Down Expand Up @@ -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_);

Expand All @@ -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());
Expand Down Expand Up @@ -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(
Expand Down
8 changes: 6 additions & 2 deletions core/src/test/java/com/alibaba/fastjson2/ListTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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));

Expand All @@ -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(
Expand Down
25 changes: 13 additions & 12 deletions core/src/test/java/com/alibaba/fastjson2/dubbo/DubboTest6.java
Original file line number Diff line number Diff line change
Expand Up @@ -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)
);

Expand Down
Original file line number Diff line number Diff line change
@@ -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';
}
}

0 comments on commit 0024891

Please sign in to comment.