Skip to content

Commit 44d36cf

Browse files
committed
improved jsonb support
1 parent 0024891 commit 44d36cf

File tree

4 files changed

+37
-240
lines changed

4 files changed

+37
-240
lines changed

core/src/main/java/com/alibaba/fastjson2/writer/FieldWriterObject.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ public boolean write(JSONWriter jsonWriter, T object) {
330330

331331
if (unwrapped) {
332332
if (value instanceof Map) {
333+
boolean jsonb = jsonWriter.jsonb;
333334
for (Map.Entry entry : (Iterable<Map.Entry>) ((Map) value).entrySet()) {
334335
String entryKey = entry.getKey().toString();
335336
Object entryValue = entry.getValue();
@@ -340,7 +341,9 @@ public boolean write(JSONWriter jsonWriter, T object) {
340341
}
341342

342343
jsonWriter.writeName(entryKey);
343-
jsonWriter.writeColon();
344+
if (!jsonb) {
345+
jsonWriter.writeColon();
346+
}
344347
if (entryValue == null) {
345348
jsonWriter.writeNull();
346349
} else {

core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterCreatorASM.java

Lines changed: 19 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -1737,7 +1737,7 @@ private void gwFieldValue(
17371737
} else if (fieldClass == List.class) {
17381738
gwFieldValueList(mwc, fieldWriter, OBJECT, i);
17391739
} else {
1740-
gwFieldValueObject(mwc, fieldWriter, OBJECT, i);
1740+
gwFieldValueObject(mwc, fieldWriter, OBJECT, i, false);
17411741
}
17421742
}
17431743

@@ -1789,7 +1789,8 @@ private void gwFieldValueObject(
17891789
MethodWriterContext mwc,
17901790
FieldWriter fieldWriter,
17911791
int OBJECT,
1792-
int i
1792+
int i,
1793+
boolean jsonb
17931794
) {
17941795
Class<?> fieldClass = fieldWriter.fieldClass;
17951796
Type fieldType = fieldWriter.fieldType;
@@ -1884,13 +1885,13 @@ private void gwFieldValueObject(
18841885
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
18851886
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
18861887
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "popPath", "(Ljava/lang/Object;)V", false);
1887-
mw.visitJumpInsn(Opcodes.GOTO, notNull_);
1888+
mw.visitJumpInsn(Opcodes.GOTO, null_);
18881889

18891890
mw.visitLabel(endDetect_);
18901891

18911892
if ("this$0".equals(fieldName) || "this$1".equals(fieldName) || "this$2".equals(fieldName)) {
18921893
mw.visitVarInsn(Opcodes.ILOAD, REF_DETECT);
1893-
mw.visitJumpInsn(Opcodes.IFEQ, notNull_);
1894+
mw.visitJumpInsn(Opcodes.IFEQ, null_);
18941895
}
18951896
}
18961897

@@ -1911,6 +1912,7 @@ private void gwFieldValueObject(
19111912
// writeFieldName(w);
19121913
gwFieldName(mwc, fieldWriter, i);
19131914

1915+
Class itemClass = fieldWriter.getItemClass();
19141916
if (fieldClass == BigDecimal.class) {
19151917
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
19161918
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
@@ -1954,6 +1956,8 @@ private void gwFieldValueObject(
19541956
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
19551957
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
19561958
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "writeString", "([Ljava/lang/String;)V", false);
1959+
} else if (fieldClass == List.class && (itemClass == String.class || itemClass == Integer.class || itemClass == Long.class)) {
1960+
gwListSimpleType(mwc, i, mw, fieldClass, itemClass, FIELD_VALUE);
19571961
} else {
19581962
// fw.getObjectWriter(w, value.getClass());
19591963
mw.visitVarInsn(Opcodes.ALOAD, THIS);
@@ -1971,17 +1975,22 @@ private void gwFieldValueObject(
19711975
mw.visitLdcInsn(fieldName);
19721976
mwc.loadFieldType(i, fieldType);
19731977
mw.visitLdcInsn(features);
1978+
1979+
String writeMethod;
1980+
if (jsonb) {
1981+
writeMethod = (features & JSONWriter.Feature.BeanToArray.mask) != 0 ? "writeArrayMappingJSONB" : "writeJSONB";
1982+
} else {
1983+
writeMethod = (features & JSONWriter.Feature.BeanToArray.mask) != 0 ? "writeArrayMapping" : "write";
1984+
}
19741985
mw.visitMethodInsn(
19751986
Opcodes.INVOKEINTERFACE,
19761987
TYPE_OBJECT_WRITER,
1977-
(features & JSONWriter.Feature.BeanToArray.mask) != 0 ? "writeArrayMapping" : "write",
1988+
writeMethod,
19781989
METHOD_DESC_WRITE_OBJECT,
19791990
true
19801991
);
19811992
}
19821993

1983-
mw.visitJumpInsn(Opcodes.GOTO, notNull_);
1984-
19851994
if (refDetection) {
19861995
int REF_DETECT = mwc.var("REF_DETECT");
19871996

@@ -1997,6 +2006,8 @@ private void gwFieldValueObject(
19972006
mw.visitLabel(endDetect_);
19982007
}
19992008

2009+
mw.visitJumpInsn(Opcodes.GOTO, notNull_);
2010+
20002011
mw.visitLabel(null_);
20012012

20022013
// if (!jw.isWriteNulls())
@@ -2234,7 +2245,7 @@ private void gwFieldValueJSONB(
22342245
} else if (fieldClass == Date.class) {
22352246
gwFieldValueDate(mwc, fieldWriter, OBJECT, i);
22362247
} else {
2237-
gwFieldValueObjectJSONB(mwc, fieldWriter, OBJECT, i);
2248+
gwFieldValueObject(mwc, fieldWriter, OBJECT, i, true);
22382249
}
22392250
}
22402251

@@ -2486,236 +2497,6 @@ private void gwFloat(
24862497
mw.visitLabel(endIfNull_);
24872498
}
24882499

2489-
private void gwFieldValueObjectJSONB(
2490-
MethodWriterContext mwc,
2491-
FieldWriter fieldWriter,
2492-
int OBJECT,
2493-
int i
2494-
) {
2495-
Class<?> fieldClass = fieldWriter.fieldClass;
2496-
Type fieldType = fieldWriter.fieldType;
2497-
String fieldName = fieldWriter.fieldName;
2498-
2499-
boolean refDetection = !ObjectWriterProvider.isNotReferenceDetect(fieldClass);
2500-
int FIELD_VALUE = mwc.var(fieldClass);
2501-
2502-
Integer REF_PATH = null;
2503-
if (refDetection) {
2504-
REF_PATH = mwc.var("REF_PATH");
2505-
}
2506-
2507-
long features = fieldWriter.features | mwc.objectFeatures;
2508-
MethodWriter mw = mwc.mw;
2509-
2510-
Label null_ = new Label(), notNull_ = new Label();
2511-
2512-
genGetObject(mwc, fieldWriter, i, OBJECT);
2513-
mw.visitInsn(Opcodes.DUP);
2514-
mw.visitVarInsn(Opcodes.ASTORE, FIELD_VALUE);
2515-
2516-
mw.visitJumpInsn(Opcodes.IFNULL, null_);
2517-
2518-
if (!Serializable.class.isAssignableFrom(fieldClass) && fieldClass != List.class) {
2519-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2520-
if (!fieldWriter.isFieldClassSerializable()) {
2521-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "isIgnoreNoneSerializable", "()Z", false);
2522-
} else {
2523-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2524-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "isIgnoreNoneSerializable", "(Ljava/lang/Object;)Z", false);
2525-
}
2526-
mw.visitJumpInsn(Opcodes.IFNE, null_);
2527-
}
2528-
2529-
/**
2530-
* boolean refDetect = jsonWriter.isRefDetect();
2531-
* if (refDetect) {
2532-
* if (value == object) {
2533-
* writeFieldName(jsonWriter);
2534-
* jsonWriter.writeReference("..");
2535-
* goto null_
2536-
* }
2537-
*
2538-
* String refPath = context.setPath(name, value);
2539-
* if (refPath != null) {
2540-
* writeFieldName(jsonWriter);
2541-
* jsonWriter.writeReference(refPath);
2542-
* context.popPath();
2543-
* goto null_
2544-
* }
2545-
* }
2546-
*/
2547-
2548-
if (refDetection) {
2549-
Label endDetect_ = new Label(), refSetPath_ = new Label();
2550-
2551-
int REF_DETECT = mwc.var("REF_DETECT");
2552-
2553-
if (fieldClass == Object.class) {
2554-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2555-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2556-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "isRefDetect", "(Ljava/lang/Object;)Z", false);
2557-
} else {
2558-
mwc.genIsEnabled(JSONWriter.Feature.ReferenceDetection.mask, null);
2559-
}
2560-
mw.visitInsn(Opcodes.DUP);
2561-
mw.visitVarInsn(Opcodes.ISTORE, REF_DETECT);
2562-
mw.visitJumpInsn(Opcodes.IFEQ, endDetect_);
2563-
2564-
mw.visitVarInsn(Opcodes.ALOAD, OBJECT);
2565-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2566-
mw.visitJumpInsn(Opcodes.IF_ACMPNE, refSetPath_);
2567-
2568-
gwFieldName(mwc, fieldWriter, i);
2569-
2570-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2571-
mw.visitLdcInsn("..");
2572-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "writeReference", "(Ljava/lang/String;)V", false);
2573-
2574-
mw.visitJumpInsn(Opcodes.GOTO, null_);
2575-
2576-
mw.visitLabel(refSetPath_);
2577-
2578-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2579-
mw.visitVarInsn(Opcodes.ALOAD, THIS);
2580-
mw.visitFieldInsn(Opcodes.GETFIELD, mwc.classNameType, fieldWriter(i), DESC_FIELD_WRITER);
2581-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2582-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "setPath", METHOD_DESC_SET_PATH2, false);
2583-
mw.visitInsn(Opcodes.DUP);
2584-
mw.visitVarInsn(Opcodes.ASTORE, REF_PATH);
2585-
mw.visitJumpInsn(Opcodes.IFNULL, endDetect_);
2586-
2587-
gwFieldName(mwc, fieldWriter, i);
2588-
2589-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2590-
mw.visitVarInsn(Opcodes.ALOAD, REF_PATH);
2591-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "writeReference", METHOD_DESC_WRITE_REFERENCE, false);
2592-
2593-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2594-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2595-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "popPath", "(Ljava/lang/Object;)V", false);
2596-
mw.visitJumpInsn(Opcodes.GOTO, null_);
2597-
2598-
mw.visitLabel(endDetect_);
2599-
2600-
if ("this$0".equals(fieldName) || "this$1".equals(fieldName) || "this$2".equals(fieldName)) {
2601-
mw.visitVarInsn(Opcodes.ILOAD, REF_DETECT);
2602-
mw.visitJumpInsn(Opcodes.IFEQ, null_);
2603-
}
2604-
}
2605-
2606-
if (Object[].class.isAssignableFrom(fieldClass)) {
2607-
Label notWriteEmptyArrayEnd_ = new Label();
2608-
mwc.genIsEnabled(JSONWriter.Feature.NotWriteEmptyArray.mask, notWriteEmptyArrayEnd_);
2609-
2610-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2611-
mw.visitTypeInsn(Opcodes.CHECKCAST, "[Ljava/lang/Object;");
2612-
mw.visitInsn(Opcodes.ARRAYLENGTH);
2613-
mw.visitJumpInsn(Opcodes.IFNE, notWriteEmptyArrayEnd_);
2614-
2615-
mw.visitJumpInsn(Opcodes.GOTO, notNull_);
2616-
2617-
mw.visitLabel(notWriteEmptyArrayEnd_);
2618-
}
2619-
2620-
gwFieldName(mwc, fieldWriter, i);
2621-
2622-
// fw.getObjectWriter(w, value.getClass());
2623-
2624-
Class itemClass = fieldWriter.getItemClass();
2625-
if (fieldClass == List.class && (itemClass == String.class || itemClass == Integer.class || itemClass == Long.class)) {
2626-
gwListSimpleType(mwc, i, mw, fieldClass, itemClass, FIELD_VALUE);
2627-
} else {
2628-
mw.visitVarInsn(Opcodes.ALOAD, THIS);
2629-
mw.visitFieldInsn(Opcodes.GETFIELD, mwc.classNameType, fieldWriter(i), DESC_FIELD_WRITER);
2630-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2631-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2632-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/lang/Object", "getClass", "()Ljava/lang/Class;", false);
2633-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL,
2634-
TYPE_FIELD_WRITER,
2635-
"getObjectWriter",
2636-
METHOD_DESC_GET_OBJECT_WRITER,
2637-
false);
2638-
2639-
// objectWriter.write(jw, ctx, value);
2640-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2641-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2642-
mw.visitLdcInsn(fieldName);
2643-
mwc.loadFieldType(i, fieldWriter.fieldType);
2644-
mw.visitLdcInsn(fieldWriter.features);
2645-
mw.visitMethodInsn(Opcodes.INVOKEINTERFACE, TYPE_OBJECT_WRITER, "writeJSONB", METHOD_DESC_WRITE_OBJECT, true);
2646-
}
2647-
2648-
if (refDetection) {
2649-
int REF_DETECT = mwc.var("REF_DETECT");
2650-
2651-
Label endDetect_ = new Label();
2652-
2653-
mw.visitVarInsn(Opcodes.ILOAD, REF_DETECT);
2654-
mw.visitJumpInsn(Opcodes.IFEQ, endDetect_);
2655-
2656-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2657-
mw.visitVarInsn(Opcodes.ALOAD, FIELD_VALUE);
2658-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, "popPath", "(Ljava/lang/Object;)V", false);
2659-
2660-
mw.visitLabel(endDetect_);
2661-
}
2662-
2663-
mw.visitJumpInsn(Opcodes.GOTO, notNull_);
2664-
2665-
mw.visitLabel(null_);
2666-
2667-
// if (!jw.isWriteNulls())
2668-
if ((features & JSONWriter.Feature.WriteNulls.mask) == 0) {
2669-
long nullFeatures = JSONWriter.Feature.WriteNulls.mask;
2670-
if (fieldClass == AtomicLongArray.class
2671-
|| fieldClass == AtomicIntegerArray.class
2672-
|| Collection.class.isAssignableFrom(fieldClass)
2673-
|| fieldClass.isArray()) {
2674-
nullFeatures |= WriteNullListAsEmpty.mask;
2675-
nullFeatures |= NullAsDefaultValue.mask;
2676-
} else if (Number.class.isAssignableFrom(fieldClass)) {
2677-
nullFeatures |= WriteNullNumberAsZero.mask;
2678-
nullFeatures |= NullAsDefaultValue.mask;
2679-
} else if (fieldClass == Boolean.class) {
2680-
nullFeatures |= WriteNullBooleanAsFalse.mask;
2681-
nullFeatures |= NullAsDefaultValue.mask;
2682-
} else if (fieldClass == String.class) {
2683-
nullFeatures |= WriteNullStringAsEmpty.mask;
2684-
nullFeatures |= NullAsDefaultValue.mask;
2685-
}
2686-
mwc.genIsEnabled(nullFeatures, notNull_);
2687-
// mw.visitVarInsn(Opcodes.ILOAD, mwc.var(WRITE_NULLS));
2688-
// mw.visitJumpInsn(Opcodes.IFEQ, notNull_);
2689-
}
2690-
2691-
// writeFieldName(w);
2692-
gwFieldName(mwc, fieldWriter, i);
2693-
2694-
// jw.writeNulll
2695-
String WRITE_NULL_METHOD;
2696-
if (fieldClass == AtomicLongArray.class
2697-
|| fieldClass == AtomicIntegerArray.class
2698-
|| Collection.class.isAssignableFrom(fieldClass)
2699-
|| fieldClass.isArray()) {
2700-
WRITE_NULL_METHOD = "writeArrayNull";
2701-
} else if (Number.class.isAssignableFrom(fieldClass)) {
2702-
WRITE_NULL_METHOD = "writeNumberNull";
2703-
} else if (fieldClass == Boolean.class) {
2704-
WRITE_NULL_METHOD = "writeBooleanNull";
2705-
} else if (fieldClass == String.class
2706-
|| fieldClass == Appendable.class
2707-
|| fieldClass == StringBuffer.class
2708-
|| fieldClass == StringBuilder.class) {
2709-
WRITE_NULL_METHOD = "writeStringNull";
2710-
} else {
2711-
WRITE_NULL_METHOD = "writeNull";
2712-
}
2713-
mw.visitVarInsn(Opcodes.ALOAD, JSON_WRITER);
2714-
mw.visitMethodInsn(Opcodes.INVOKEVIRTUAL, TYPE_JSON_WRITER, WRITE_NULL_METHOD, "()V", false);
2715-
2716-
mw.visitLabel(notNull_);
2717-
}
2718-
27192500
private static void gwListSimpleType(
27202501
MethodWriterContext mwc,
27212502
int i,

core/src/test/java/com/alibaba/fastjson2/features/UnwrappedTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.alibaba.fastjson2.features;
22

33
import com.alibaba.fastjson2.JSON;
4+
import com.alibaba.fastjson2.JSONB;
45
import com.alibaba.fastjson2.JSONReader;
56
import com.alibaba.fastjson2.annotation.JSONField;
67
import org.junit.jupiter.api.Test;
@@ -18,6 +19,18 @@ public void test() {
1819
bean.properties.put("attr1", "val1");
1920
String str = JSON.toJSONString(bean);
2021
assertEquals("{\"name\":\"My bean\",\"attr1\":\"val1\"}", str);
22+
23+
ExtendableBean bean1 = JSON.parseObject(str, ExtendableBean.class);
24+
assertEquals(bean.properties, bean1.properties);
25+
26+
byte[] jsonb = JSONB.toBytes(bean);
27+
assertEquals("{\n" +
28+
"\t\"name\":\"My bean\",\n" +
29+
"\t\"attr1\":\"val1\"\n" +
30+
"}", JSONB.toJSONString(jsonb));
31+
32+
ExtendableBean bean2 = JSONB.parseObject(jsonb, ExtendableBean.class);
33+
assertEquals(bean.properties, bean2.properties);
2134
}
2235

2336
public static class ExtendableBean {

fastjson1-compatible/src/test/java/com/alibaba/fastjson/issue_3400/Issue_20201016_01.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public void testToString() {
2424
return;
2525
}
2626

27-
assertEquals("{\"agent\":null,\"creator\":{\"account\":\"account\",\"name\":\"name\",\"workid\":null},\"owner\":{\"account\":\"account\",\"name\":\"name\",\"workid\":\"\"}}", s);
27+
assertEquals("{\"agent\":null,\"creator\":{\"account\":\"account\",\"name\":\"name\",\"workid\":null},\"owner\":{\"$ref\":\"$.creator\"},\"owner\":null}", s);
2828
}
2929

3030
@Test

0 commit comments

Comments
 (0)