Skip to content

Commit 077c4d5

Browse files
yuxin-miaomxyns
andauthored
feat - record support with canonical constructor (#5) (PR #13)
* tmp - record investigation / debugging * test - empty record read * test - tests showing different object decoder implementation use Co-authored-by: Maxou <[email protected]> Co-authored-by: xinmiao <>
1 parent 6927007 commit 077c4d5

File tree

6 files changed

+366
-30
lines changed

6 files changed

+366
-30
lines changed

src/main/java/com/jsoniter/ReflectionDecoderFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ public static Decoder create(ClassInfo classAndArgs) {
2424
return new ReflectionEnumDecoder(clazz);
2525
}
2626
if (clazz.isRecord()) {
27-
return new ReflectionRecordDecoder(clazz, typeArgs);
27+
return new ReflectionRecordDecoder(classAndArgs).create();
2828
}
2929
return new ReflectionObjectDecoder(classAndArgs).create();
3030
}

src/main/java/com/jsoniter/ReflectionObjectDecoder.java

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@
99

1010
class ReflectionObjectDecoder {
1111

12-
private static Object NOT_SET = new Object() {
12+
protected static Object NOT_SET = new Object() {
1313
@Override
1414
public String toString() {
1515
return "NOT_SET";
1616
}
1717
};
18-
private Map<Slice, Binding> allBindings = new HashMap<Slice, Binding>();
19-
private String tempCacheKey;
20-
private String ctorArgsCacheKey;
21-
private int tempCount;
22-
private long expectedTracker;
23-
private int requiredIdx;
24-
private int tempIdx;
25-
private ClassDescriptor desc;
18+
protected Map<Slice, Binding> allBindings = new HashMap<Slice, Binding>();
19+
protected String tempCacheKey;
20+
protected String ctorArgsCacheKey;
21+
protected int tempCount;
22+
protected long expectedTracker;
23+
protected int requiredIdx;
24+
protected int tempIdx;
25+
protected ClassDescriptor desc;
2626

2727
public ReflectionObjectDecoder(ClassInfo classInfo) {
2828
try {
@@ -34,7 +34,9 @@ public ReflectionObjectDecoder(ClassInfo classInfo) {
3434
}
3535
}
3636

37-
private final void init(ClassInfo classInfo) throws Exception {
37+
protected final void init(ClassInfo classInfo) throws Exception {
38+
39+
System.out.println("INIT");
3840
Class clazz = classInfo.clazz;
3941
ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(classInfo, true);
4042
for (Binding param : desc.ctor.parameters) {
@@ -116,6 +118,7 @@ public class OnlyField implements Decoder {
116118

117119
public Object decode(JsonIterator iter) throws IOException {
118120
try {
121+
System.out.println("ONLY FIELD");
119122
return decode_(iter);
120123
} catch (RuntimeException e) {
121124
throw e;
@@ -181,6 +184,7 @@ public class WithCtor implements Decoder {
181184
@Override
182185
public Object decode(JsonIterator iter) throws IOException {
183186
try {
187+
System.out.println("WITH CTOR");
184188
return decode_(iter);
185189
} catch (RuntimeException e) {
186190
throw e;
@@ -260,6 +264,7 @@ public class WithWrapper implements Decoder {
260264
@Override
261265
public Object decode(JsonIterator iter) throws IOException {
262266
try {
267+
System.out.println("WITH WRAPPER");
263268
return decode_(iter);
264269
} catch (RuntimeException e) {
265270
throw e;
@@ -346,7 +351,7 @@ private void setToBinding(Object obj, Binding binding, Object value) throws Exce
346351
}
347352
}
348353

349-
private void setExtra(Object obj, Map<String, Object> extra) throws Exception {
354+
protected void setExtra(Object obj, Map<String, Object> extra) throws Exception {
350355
if (extra == null) {
351356
return;
352357
}
@@ -367,24 +372,24 @@ private void setExtra(Object obj, Map<String, Object> extra) throws Exception {
367372
}
368373
}
369374

370-
private boolean canNotSetDirectly(Binding binding) {
375+
protected boolean canNotSetDirectly(Binding binding) {
371376
return binding.field == null && binding.method == null;
372377
}
373378

374-
private Object decodeBinding(JsonIterator iter, Binding binding) throws Exception {
379+
protected Object decodeBinding(JsonIterator iter, Binding binding) throws Exception {
375380
Object value;
376381
value = binding.decoder.decode(iter);
377382
return value;
378383
}
379384

380-
private Object decodeBinding(JsonIterator iter, Object obj, Binding binding) throws Exception {
385+
protected Object decodeBinding(JsonIterator iter, Object obj, Binding binding) throws Exception {
381386
if (binding.valueCanReuse) {
382387
CodegenAccess.setExistingObject(iter, binding.field.get(obj));
383388
}
384389
return decodeBinding(iter, binding);
385390
}
386391

387-
private Map<String, Object> onUnknownProperty(JsonIterator iter, Slice fieldName, Map<String, Object> extra) throws IOException {
392+
protected Map<String, Object> onUnknownProperty(JsonIterator iter, Slice fieldName, Map<String, Object> extra) throws IOException {
388393
boolean shouldReadValue = desc.asExtraForUnknownProperties || !desc.keyValueTypeWrappers.isEmpty();
389394
if (shouldReadValue) {
390395
Any value = iter.readAny();
@@ -398,7 +403,7 @@ private Map<String, Object> onUnknownProperty(JsonIterator iter, Slice fieldName
398403
return extra;
399404
}
400405

401-
private List<String> collectMissingFields(long tracker) {
406+
protected List<String> collectMissingFields(long tracker) {
402407
List<String> missingFields = new ArrayList<String>();
403408
for (Binding binding : allBindings.values()) {
404409
if (binding.asMissingWhenNotPresent) {
@@ -409,7 +414,7 @@ private List<String> collectMissingFields(long tracker) {
409414
return missingFields;
410415
}
411416

412-
private void applyWrappers(Object[] temp, Object obj) throws Exception {
417+
protected void applyWrappers(Object[] temp, Object obj) throws Exception {
413418
for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) {
414419
Object[] args = new Object[wrapper.parameters.size()];
415420
for (int i = 0; i < wrapper.parameters.size(); i++) {
@@ -422,7 +427,7 @@ private void applyWrappers(Object[] temp, Object obj) throws Exception {
422427
}
423428
}
424429

425-
private Object createNewObject(JsonIterator iter, Object[] temp) throws Exception {
430+
protected Object createNewObject(JsonIterator iter, Object[] temp) throws Exception {
426431
if (iter.tempObjects == null) {
427432
iter.tempObjects = new HashMap<String, Object>();
428433
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
package com.jsoniter;
2+
3+
import com.jsoniter.spi.*;
4+
5+
import java.io.IOException;
6+
import java.util.Arrays;
7+
import java.util.HashMap;
8+
import java.util.Map;
9+
10+
public class ReflectionRecordDecoder extends ReflectionObjectDecoder {
11+
12+
private boolean useOnlyFieldRecord = false;
13+
14+
public ReflectionRecordDecoder(ClassInfo classInfo) {
15+
16+
super(classInfo);
17+
18+
if (desc.clazz.isRecord() && !desc.fields.isEmpty() && tempCount == 0) {
19+
tempCount = tempIdx;
20+
tempCacheKey = "temp@" + desc.clazz.getName();
21+
ctorArgsCacheKey = "ctor@" + desc.clazz.getName();
22+
23+
desc.ctor.parameters.addAll(desc.fields);
24+
useOnlyFieldRecord = true;
25+
}
26+
}
27+
28+
@Override
29+
public Decoder create() {
30+
31+
if (useOnlyFieldRecord)
32+
return new OnlyFieldRecord();
33+
34+
if (desc.ctor.parameters.isEmpty()) {
35+
if (desc.bindingTypeWrappers.isEmpty()) {
36+
return new OnlyFieldRecord();
37+
} else {
38+
return new WithWrapper();
39+
}
40+
} else {
41+
return new WithCtor();
42+
}
43+
}
44+
45+
public class OnlyFieldRecord implements Decoder {
46+
47+
@Override
48+
public Object decode(JsonIterator iter) throws IOException {
49+
50+
try {
51+
System.out.println("ONLY FIELD RECORD");
52+
return decode_(iter);
53+
} catch (RuntimeException e) {
54+
throw e;
55+
} catch (Exception e) {
56+
throw new JsonException(e);
57+
}
58+
}
59+
60+
private Object decode_(JsonIterator iter) throws Exception {
61+
if (iter.readNull()) {
62+
CodegenAccess.resetExistingObject(iter);
63+
return null;
64+
}
65+
if (iter.tempObjects == null) {
66+
iter.tempObjects = new HashMap<String, Object>();
67+
}
68+
Object[] temp = (Object[]) iter.tempObjects.get(tempCacheKey);
69+
if (temp == null) {
70+
temp = new Object[tempCount];
71+
iter.tempObjects.put(tempCacheKey, temp);
72+
}
73+
Arrays.fill(temp, NOT_SET);
74+
if (!CodegenAccess.readObjectStart(iter)) {
75+
if (requiredIdx > 0) {
76+
throw new JsonException("missing required properties: " + collectMissingFields(0));
77+
}
78+
return createNewObject(iter, temp);
79+
}
80+
Map<String, Object> extra = null;
81+
long tracker = 0L;
82+
Slice fieldName = CodegenAccess.readObjectFieldAsSlice(iter);
83+
Binding binding = allBindings.get(fieldName);
84+
if (binding == null) {
85+
extra = onUnknownProperty(iter, fieldName, extra);
86+
} else {
87+
if (binding.asMissingWhenNotPresent) {
88+
tracker |= binding.mask;
89+
}
90+
temp[binding.idx] = decodeBinding(iter, binding);
91+
}
92+
while (CodegenAccess.nextToken(iter) == ',') {
93+
fieldName = CodegenAccess.readObjectFieldAsSlice(iter);
94+
binding = allBindings.get(fieldName);
95+
if (binding == null) {
96+
extra = onUnknownProperty(iter, fieldName, extra);
97+
} else {
98+
if (binding.asMissingWhenNotPresent) {
99+
tracker |= binding.mask;
100+
}
101+
temp[binding.idx] = decodeBinding(iter, binding);
102+
}
103+
}
104+
if (tracker != expectedTracker) {
105+
throw new JsonException("missing required properties: " + collectMissingFields(tracker));
106+
}
107+
Object obj = createNewObject(iter, temp.clone());
108+
setExtra(obj, extra);
109+
applyWrappers(temp, obj);
110+
return obj;
111+
}
112+
113+
}
114+
}

src/main/java/com/jsoniter/spi/ClassDescriptor.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public static ClassDescriptor getDecodingClassDescriptor(ClassInfo classInfo, bo
3131
desc.classInfo = classInfo;
3232
desc.clazz = clazz;
3333
desc.lookup = lookup;
34-
desc.ctor = getCtor(clazz);
34+
desc.ctor = clazz.isRecord() ? getRecordCtor(clazz) : getCtor(clazz);
3535
desc.setters = getSetters(lookup, classInfo, includingPrivate);
3636
desc.getters = new ArrayList<Binding>();
3737
desc.fields = getFields(lookup, classInfo, includingPrivate);
@@ -203,6 +203,20 @@ private static ConstructorDescriptor getCtor(Class clazz) {
203203
return cctor;
204204
}
205205

206+
private static ConstructorDescriptor getRecordCtor(Class clazz) {
207+
ConstructorDescriptor cctor = new ConstructorDescriptor();
208+
try {
209+
Constructor<?> ctor = clazz.getDeclaredConstructors()[0];
210+
cctor.ctor = ctor;
211+
for (Type parameter : ctor.getParameterTypes()) {
212+
ClassInfo info = new ClassInfo(parameter);
213+
}
214+
} catch (Exception e) {
215+
cctor.ctor = null;
216+
}
217+
return cctor;
218+
}
219+
206220
private static List<Binding> getFields(Map<String, Type> lookup, ClassInfo classInfo, boolean includingPrivate) {
207221
ArrayList<Binding> bindings = new ArrayList<Binding>();
208222
for (Field field : getAllFields(classInfo.clazz)) {
@@ -432,7 +446,6 @@ public List<Binding> allDecoderBindings() {
432446
return bindings;
433447
}
434448

435-
436449
public List<Binding> allEncoderBindings() {
437450
ArrayList<Binding> bindings = new ArrayList<Binding>(8);
438451
bindings.addAll(fields);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.jsoniter;
2+
3+
public record SimpleRecord(String field1, String field2) {
4+
public SimpleRecord() {
5+
this(null, null);
6+
}
7+
public SimpleRecord(String field1, String field2) {
8+
this.field1 = field1;
9+
this.field2 = field2;
10+
}
11+
}

0 commit comments

Comments
 (0)