Skip to content

Commit

Permalink
feat - record support with canonical constructor (#5) (PR #13)
Browse files Browse the repository at this point in the history
* 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 <>
  • Loading branch information
yuxin-miao and mxyns authored Mar 4, 2022
1 parent 6927007 commit 077c4d5
Show file tree
Hide file tree
Showing 6 changed files with 366 additions and 30 deletions.
2 changes: 1 addition & 1 deletion src/main/java/com/jsoniter/ReflectionDecoderFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public static Decoder create(ClassInfo classAndArgs) {
return new ReflectionEnumDecoder(clazz);
}
if (clazz.isRecord()) {
return new ReflectionRecordDecoder(clazz, typeArgs);
return new ReflectionRecordDecoder(classAndArgs).create();
}
return new ReflectionObjectDecoder(classAndArgs).create();
}
Expand Down
41 changes: 23 additions & 18 deletions src/main/java/com/jsoniter/ReflectionObjectDecoder.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,20 @@

class ReflectionObjectDecoder {

private static Object NOT_SET = new Object() {
protected static Object NOT_SET = new Object() {
@Override
public String toString() {
return "NOT_SET";
}
};
private Map<Slice, Binding> allBindings = new HashMap<Slice, Binding>();
private String tempCacheKey;
private String ctorArgsCacheKey;
private int tempCount;
private long expectedTracker;
private int requiredIdx;
private int tempIdx;
private ClassDescriptor desc;
protected Map<Slice, Binding> allBindings = new HashMap<Slice, Binding>();
protected String tempCacheKey;
protected String ctorArgsCacheKey;
protected int tempCount;
protected long expectedTracker;
protected int requiredIdx;
protected int tempIdx;
protected ClassDescriptor desc;

public ReflectionObjectDecoder(ClassInfo classInfo) {
try {
Expand All @@ -34,7 +34,9 @@ public ReflectionObjectDecoder(ClassInfo classInfo) {
}
}

private final void init(ClassInfo classInfo) throws Exception {
protected final void init(ClassInfo classInfo) throws Exception {

System.out.println("INIT");
Class clazz = classInfo.clazz;
ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(classInfo, true);
for (Binding param : desc.ctor.parameters) {
Expand Down Expand Up @@ -116,6 +118,7 @@ public class OnlyField implements Decoder {

public Object decode(JsonIterator iter) throws IOException {
try {
System.out.println("ONLY FIELD");
return decode_(iter);
} catch (RuntimeException e) {
throw e;
Expand Down Expand Up @@ -181,6 +184,7 @@ public class WithCtor implements Decoder {
@Override
public Object decode(JsonIterator iter) throws IOException {
try {
System.out.println("WITH CTOR");
return decode_(iter);
} catch (RuntimeException e) {
throw e;
Expand Down Expand Up @@ -260,6 +264,7 @@ public class WithWrapper implements Decoder {
@Override
public Object decode(JsonIterator iter) throws IOException {
try {
System.out.println("WITH WRAPPER");
return decode_(iter);
} catch (RuntimeException e) {
throw e;
Expand Down Expand Up @@ -346,7 +351,7 @@ private void setToBinding(Object obj, Binding binding, Object value) throws Exce
}
}

private void setExtra(Object obj, Map<String, Object> extra) throws Exception {
protected void setExtra(Object obj, Map<String, Object> extra) throws Exception {
if (extra == null) {
return;
}
Expand All @@ -367,24 +372,24 @@ private void setExtra(Object obj, Map<String, Object> extra) throws Exception {
}
}

private boolean canNotSetDirectly(Binding binding) {
protected boolean canNotSetDirectly(Binding binding) {
return binding.field == null && binding.method == null;
}

private Object decodeBinding(JsonIterator iter, Binding binding) throws Exception {
protected Object decodeBinding(JsonIterator iter, Binding binding) throws Exception {
Object value;
value = binding.decoder.decode(iter);
return value;
}

private Object decodeBinding(JsonIterator iter, Object obj, Binding binding) throws Exception {
protected Object decodeBinding(JsonIterator iter, Object obj, Binding binding) throws Exception {
if (binding.valueCanReuse) {
CodegenAccess.setExistingObject(iter, binding.field.get(obj));
}
return decodeBinding(iter, binding);
}

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

private List<String> collectMissingFields(long tracker) {
protected List<String> collectMissingFields(long tracker) {
List<String> missingFields = new ArrayList<String>();
for (Binding binding : allBindings.values()) {
if (binding.asMissingWhenNotPresent) {
Expand All @@ -409,7 +414,7 @@ private List<String> collectMissingFields(long tracker) {
return missingFields;
}

private void applyWrappers(Object[] temp, Object obj) throws Exception {
protected void applyWrappers(Object[] temp, Object obj) throws Exception {
for (WrapperDescriptor wrapper : desc.bindingTypeWrappers) {
Object[] args = new Object[wrapper.parameters.size()];
for (int i = 0; i < wrapper.parameters.size(); i++) {
Expand All @@ -422,7 +427,7 @@ private void applyWrappers(Object[] temp, Object obj) throws Exception {
}
}

private Object createNewObject(JsonIterator iter, Object[] temp) throws Exception {
protected Object createNewObject(JsonIterator iter, Object[] temp) throws Exception {
if (iter.tempObjects == null) {
iter.tempObjects = new HashMap<String, Object>();
}
Expand Down
114 changes: 114 additions & 0 deletions src/main/java/com/jsoniter/ReflectionRecordDecoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.jsoniter;

import com.jsoniter.spi.*;

import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class ReflectionRecordDecoder extends ReflectionObjectDecoder {

private boolean useOnlyFieldRecord = false;

public ReflectionRecordDecoder(ClassInfo classInfo) {

super(classInfo);

if (desc.clazz.isRecord() && !desc.fields.isEmpty() && tempCount == 0) {
tempCount = tempIdx;
tempCacheKey = "temp@" + desc.clazz.getName();
ctorArgsCacheKey = "ctor@" + desc.clazz.getName();

desc.ctor.parameters.addAll(desc.fields);
useOnlyFieldRecord = true;
}
}

@Override
public Decoder create() {

if (useOnlyFieldRecord)
return new OnlyFieldRecord();

if (desc.ctor.parameters.isEmpty()) {
if (desc.bindingTypeWrappers.isEmpty()) {
return new OnlyFieldRecord();
} else {
return new WithWrapper();
}
} else {
return new WithCtor();
}
}

public class OnlyFieldRecord implements Decoder {

@Override
public Object decode(JsonIterator iter) throws IOException {

try {
System.out.println("ONLY FIELD RECORD");
return decode_(iter);
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new JsonException(e);
}
}

private Object decode_(JsonIterator iter) throws Exception {
if (iter.readNull()) {
CodegenAccess.resetExistingObject(iter);
return null;
}
if (iter.tempObjects == null) {
iter.tempObjects = new HashMap<String, Object>();
}
Object[] temp = (Object[]) iter.tempObjects.get(tempCacheKey);
if (temp == null) {
temp = new Object[tempCount];
iter.tempObjects.put(tempCacheKey, temp);
}
Arrays.fill(temp, NOT_SET);
if (!CodegenAccess.readObjectStart(iter)) {
if (requiredIdx > 0) {
throw new JsonException("missing required properties: " + collectMissingFields(0));
}
return createNewObject(iter, temp);
}
Map<String, Object> extra = null;
long tracker = 0L;
Slice fieldName = CodegenAccess.readObjectFieldAsSlice(iter);
Binding binding = allBindings.get(fieldName);
if (binding == null) {
extra = onUnknownProperty(iter, fieldName, extra);
} else {
if (binding.asMissingWhenNotPresent) {
tracker |= binding.mask;
}
temp[binding.idx] = decodeBinding(iter, binding);
}
while (CodegenAccess.nextToken(iter) == ',') {
fieldName = CodegenAccess.readObjectFieldAsSlice(iter);
binding = allBindings.get(fieldName);
if (binding == null) {
extra = onUnknownProperty(iter, fieldName, extra);
} else {
if (binding.asMissingWhenNotPresent) {
tracker |= binding.mask;
}
temp[binding.idx] = decodeBinding(iter, binding);
}
}
if (tracker != expectedTracker) {
throw new JsonException("missing required properties: " + collectMissingFields(tracker));
}
Object obj = createNewObject(iter, temp.clone());
setExtra(obj, extra);
applyWrappers(temp, obj);
return obj;
}

}
}
17 changes: 15 additions & 2 deletions src/main/java/com/jsoniter/spi/ClassDescriptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public static ClassDescriptor getDecodingClassDescriptor(ClassInfo classInfo, bo
desc.classInfo = classInfo;
desc.clazz = clazz;
desc.lookup = lookup;
desc.ctor = getCtor(clazz);
desc.ctor = clazz.isRecord() ? getRecordCtor(clazz) : getCtor(clazz);
desc.setters = getSetters(lookup, classInfo, includingPrivate);
desc.getters = new ArrayList<Binding>();
desc.fields = getFields(lookup, classInfo, includingPrivate);
Expand Down Expand Up @@ -203,6 +203,20 @@ private static ConstructorDescriptor getCtor(Class clazz) {
return cctor;
}

private static ConstructorDescriptor getRecordCtor(Class clazz) {
ConstructorDescriptor cctor = new ConstructorDescriptor();
try {
Constructor<?> ctor = clazz.getDeclaredConstructors()[0];
cctor.ctor = ctor;
for (Type parameter : ctor.getParameterTypes()) {
ClassInfo info = new ClassInfo(parameter);
}
} catch (Exception e) {
cctor.ctor = null;
}
return cctor;
}

private static List<Binding> getFields(Map<String, Type> lookup, ClassInfo classInfo, boolean includingPrivate) {
ArrayList<Binding> bindings = new ArrayList<Binding>();
for (Field field : getAllFields(classInfo.clazz)) {
Expand Down Expand Up @@ -432,7 +446,6 @@ public List<Binding> allDecoderBindings() {
return bindings;
}


public List<Binding> allEncoderBindings() {
ArrayList<Binding> bindings = new ArrayList<Binding>(8);
bindings.addAll(fields);
Expand Down
11 changes: 11 additions & 0 deletions src/test/java/com/jsoniter/SimpleRecord.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.jsoniter;

public record SimpleRecord(String field1, String field2) {
public SimpleRecord() {
this(null, null);
}
public SimpleRecord(String field1, String field2) {
this.field1 = field1;
this.field2 = field2;
}
}
Loading

0 comments on commit 077c4d5

Please sign in to comment.