diff --git a/pom.xml b/pom.xml
index 4840503f..fe0bd30c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -80,6 +80,12 @@
1.20
test
+
+ org.apache.commons
+ commons-lang3
+ 3.5
+ test
+
@@ -121,8 +127,7 @@
maven-compiler-plugin
3.7.0
- 1.6
- 1.6
+ 16
UTF-8
@@ -150,7 +155,7 @@
jar
- -Xdoclint:none
+ none
@@ -196,6 +201,7 @@
maven-surefire-plugin
2.21.0
+ true
methods
1
false
diff --git a/src/main/java/com/jsoniter/ReflectionDecoderFactory.java b/src/main/java/com/jsoniter/ReflectionDecoderFactory.java
index 65dee380..2aaebc1d 100644
--- a/src/main/java/com/jsoniter/ReflectionDecoderFactory.java
+++ b/src/main/java/com/jsoniter/ReflectionDecoderFactory.java
@@ -23,6 +23,9 @@ public static Decoder create(ClassInfo classAndArgs) {
if (clazz.isEnum()) {
return new ReflectionEnumDecoder(clazz);
}
+ if (clazz.isRecord()) {
+ return new ReflectionRecordDecoder(classAndArgs).create();
+ }
return new ReflectionObjectDecoder(classAndArgs).create();
}
}
diff --git a/src/main/java/com/jsoniter/ReflectionObjectDecoder.java b/src/main/java/com/jsoniter/ReflectionObjectDecoder.java
index e1e76f73..2297b719 100644
--- a/src/main/java/com/jsoniter/ReflectionObjectDecoder.java
+++ b/src/main/java/com/jsoniter/ReflectionObjectDecoder.java
@@ -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 allBindings = new HashMap();
- private String tempCacheKey;
- private String ctorArgsCacheKey;
- private int tempCount;
- private long expectedTracker;
- private int requiredIdx;
- private int tempIdx;
- private ClassDescriptor desc;
+ protected Map allBindings = new HashMap();
+ 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 {
@@ -34,7 +34,8 @@ public ReflectionObjectDecoder(ClassInfo classInfo) {
}
}
- private final void init(ClassInfo classInfo) throws Exception {
+ protected final void init(ClassInfo classInfo) throws Exception {
+
Class clazz = classInfo.clazz;
ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(classInfo, true);
for (Binding param : desc.ctor.parameters) {
@@ -346,7 +347,7 @@ private void setToBinding(Object obj, Binding binding, Object value) throws Exce
}
}
- private void setExtra(Object obj, Map extra) throws Exception {
+ protected void setExtra(Object obj, Map extra) throws Exception {
if (extra == null) {
return;
}
@@ -367,24 +368,24 @@ private void setExtra(Object obj, Map 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 onUnknownProperty(JsonIterator iter, Slice fieldName, Map extra) throws IOException {
+ protected Map onUnknownProperty(JsonIterator iter, Slice fieldName, Map extra) throws IOException {
boolean shouldReadValue = desc.asExtraForUnknownProperties || !desc.keyValueTypeWrappers.isEmpty();
if (shouldReadValue) {
Any value = iter.readAny();
@@ -398,7 +399,7 @@ private Map onUnknownProperty(JsonIterator iter, Slice fieldName
return extra;
}
- private List collectMissingFields(long tracker) {
+ protected List collectMissingFields(long tracker) {
List missingFields = new ArrayList();
for (Binding binding : allBindings.values()) {
if (binding.asMissingWhenNotPresent) {
@@ -409,7 +410,7 @@ private List 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++) {
@@ -422,7 +423,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();
}
diff --git a/src/main/java/com/jsoniter/ReflectionRecordDecoder.java b/src/main/java/com/jsoniter/ReflectionRecordDecoder.java
new file mode 100644
index 00000000..9c71717a
--- /dev/null
+++ b/src/main/java/com/jsoniter/ReflectionRecordDecoder.java
@@ -0,0 +1,113 @@
+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 {
+ 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();
+ }
+ 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 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;
+ }
+
+ }
+}
diff --git a/src/main/java/com/jsoniter/spi/ClassDescriptor.java b/src/main/java/com/jsoniter/spi/ClassDescriptor.java
index a47dbe5d..1cd43195 100644
--- a/src/main/java/com/jsoniter/spi/ClassDescriptor.java
+++ b/src/main/java/com/jsoniter/spi/ClassDescriptor.java
@@ -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();
desc.fields = getFields(lookup, classInfo, includingPrivate);
@@ -203,6 +203,17 @@ private static ConstructorDescriptor getCtor(Class clazz) {
return cctor;
}
+ private static ConstructorDescriptor getRecordCtor(Class> clazz) {
+ ConstructorDescriptor cctor = new ConstructorDescriptor();
+ try {
+ Class>[] canonicalParameterTypes = Arrays.stream(clazz.getRecordComponents()).map(RecordComponent::getType).toArray(Class>[]::new);
+ cctor.ctor = clazz.getDeclaredConstructor(canonicalParameterTypes);
+ } catch (Exception e) {
+ cctor.ctor = null;
+ }
+ return cctor;
+ }
+
private static List getFields(Map lookup, ClassInfo classInfo, boolean includingPrivate) {
ArrayList bindings = new ArrayList();
for (Field field : getAllFields(classInfo.clazz)) {
@@ -432,7 +443,6 @@ public List allDecoderBindings() {
return bindings;
}
-
public List allEncoderBindings() {
ArrayList bindings = new ArrayList(8);
bindings.addAll(fields);
diff --git a/src/test/java/com/jsoniter/IterImplForStreamingTest.java b/src/test/java/com/jsoniter/IterImplForStreamingTest.java
index e0432d39..a66802d4 100644
--- a/src/test/java/com/jsoniter/IterImplForStreamingTest.java
+++ b/src/test/java/com/jsoniter/IterImplForStreamingTest.java
@@ -6,8 +6,8 @@
import java.io.IOException;
import java.io.InputStream;
import junit.framework.TestCase;
+import org.apache.commons.lang3.NotImplementedException;
import org.junit.experimental.categories.Category;
-import sun.reflect.generics.reflectiveObjects.NotImplementedException;
public class IterImplForStreamingTest extends TestCase {
@@ -77,7 +77,7 @@ private static InputStream getSluggishInputStream(final byte[] src) {
@Override
public int read() throws IOException {
- throw new NotImplementedException();
+ throw new NotImplementedException(new Exception());
}
@Override
diff --git a/src/test/java/com/jsoniter/SimpleRecord.java b/src/test/java/com/jsoniter/SimpleRecord.java
new file mode 100644
index 00000000..7a740468
--- /dev/null
+++ b/src/test/java/com/jsoniter/SimpleRecord.java
@@ -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;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/com/jsoniter/TestRecord.java b/src/test/java/com/jsoniter/TestRecord.java
new file mode 100644
index 00000000..423ac810
--- /dev/null
+++ b/src/test/java/com/jsoniter/TestRecord.java
@@ -0,0 +1,258 @@
+package com.jsoniter;
+
+
+import com.jsoniter.annotation.JsonCreator;
+import com.jsoniter.annotation.JsonProperty;
+import com.jsoniter.any.Any;
+import com.jsoniter.spi.ClassDescriptor;
+import com.jsoniter.spi.ClassInfo;
+import com.jsoniter.spi.JsonException;
+import junit.framework.TestCase;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+
+public class TestRecord extends TestCase {
+
+ record TestRecord1(long field1) {
+ }
+
+ public record TestRecord0(Long id, String name) {
+
+ public TestRecord0() {
+
+ this(0L, "");
+ }
+ }
+
+ // remove "disabled" to run the function
+ public void disabled_test_print_record_reflection_info() {
+
+ Class clazz = TestRecord1.class;
+
+ System.out.println("Record Constructors :");
+ for (Constructor> constructor : clazz.getConstructors()) {
+ System.out.println(constructor);
+ }
+
+ System.out.println("Record Methods : ");
+ for (Method method : clazz.getMethods()) {
+ System.out.println(method);
+ }
+
+ System.out.println("Record Fields : ");
+ for (Field field : clazz.getFields()) {
+ System.out.println(field);
+ System.out.println(" modifiers : " + Modifier.toString(field.getModifiers()));
+ }
+
+ System.out.println("Record Declared Fields : ");
+ for (Field field : clazz.getDeclaredFields()) {
+ System.out.println(field);
+ System.out.println(" modifiers : " + Modifier.toString(field.getModifiers()));
+ }
+
+ try {
+ System.out.println("Record Default Declared Constructor : " + clazz.getDeclaredConstructor());
+ } catch (Exception ex) {
+ System.err.println("No Record Default Declared Constructor!");
+ }
+
+ System.out.println("Record Declared Constructors : ");
+ for (Constructor> constructor : clazz.getDeclaredConstructors()) {
+ System.out.println(constructor);
+ System.out.println(" name : " + constructor.getName());
+ System.out.println(" modifiers : " + Modifier.toString(constructor.getModifiers()));
+ System.out.println(" input count : " + constructor.getParameterCount());
+ System.out.println(" input types : ");
+ for (Class> parameter : constructor.getParameterTypes())
+ System.out.println(" " + parameter);
+ }
+ }
+
+ public void test_empty_record() throws IOException {
+
+ JsonIterator iter = JsonIterator.parse("{}");
+ assertNotNull(iter.read(TestRecord0.class));
+ }
+
+ public void test_empty_simple_record() throws IOException {
+
+ JsonIterator iter = JsonIterator.parse("{}");
+ SimpleRecord simpleRecord = iter.read(SimpleRecord.class);
+ assertNull(simpleRecord.field1());
+ iter.reset(iter.buf);
+ Object obj = iter.read(Object.class);
+ assertEquals(0, ((Map) obj).size());
+ iter.reset(iter.buf);
+ Any any = iter.readAny();
+ assertEquals(0, any.size());
+ }
+
+ public void test_record_error() throws IOException {
+
+ JsonIterator iter = JsonIterator.parse("{ 'field1' : 1 }".replace('\'', '"'));
+ try {
+ TestRecord1 rec = iter.read(TestRecord1.class);
+ assertEquals(1, rec.field1);
+ } catch (JsonException e) {
+ throw new JsonException("no constructor for: class com.jsoniter.TestRecord", e);
+ }
+ }
+
+ public void test_record_withOnlyFieldDecoder() throws IOException {
+
+ assertEquals(ReflectionRecordDecoder.OnlyFieldRecord.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecord1.class)).getClass());
+
+ JsonIterator iter = JsonIterator.parse("{ 'field1' : 1 }".replace('\'', '"'));
+ TestRecord1 record = iter.read(TestRecord1.class);
+
+ assertEquals(1, record.field1);
+ }
+
+ public void test_record_2_fields_withOnlyFieldDecoder() throws IOException {
+
+ record TestRecord2(long field1, String field2) {
+ }
+
+ assertEquals(ReflectionRecordDecoder.OnlyFieldRecord.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecord2.class)).getClass());
+
+ JsonIterator iter = JsonIterator.parse("{ 'field1' : 1, 'field2' : 'hey' }".replace('\'', '"'));
+ TestRecord2 record = iter.read(TestRecord2.class);
+
+ assertEquals(1, record.field1);
+ assertEquals("hey", record.field2);
+ }
+
+ public void test_record_2_fields_swapFieldOrder_withOnlyFieldDecoder() throws IOException {
+
+ record TestRecord2(String field2, long field1) {
+ }
+
+ assertEquals(ReflectionRecordDecoder.OnlyFieldRecord.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecord2.class)).getClass());
+
+ JsonIterator iter = JsonIterator.parse("{ 'field2' : 'hey', 'field1' : 1 }".replace('\'', '"'));
+ TestRecord2 record = iter.read(TestRecord2.class);
+
+ assertEquals(1, record.field1);
+ assertEquals("hey", record.field2);
+ }
+
+ public void test_record_recordComposition_withOnlyFieldDecoder() throws IOException {
+
+ record TestRecordA(long fieldA) {
+ }
+ record TestRecordB(long fieldB, TestRecordA a) {
+ }
+
+ assertEquals(ReflectionRecordDecoder.OnlyFieldRecord.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecordB.class)).getClass());
+
+ JsonIterator iter = JsonIterator.parse("{ 'fieldB' : 1, 'a' : { 'fieldA' : 69 } }".replace('\'', '"'));
+ TestRecordB record = iter.read(TestRecordB.class);
+
+ assertEquals(1, record.fieldB);
+ assertEquals(69, record.a.fieldA);
+ }
+
+ public void test_record_empty_constructor_withOnlyFieldDecoder() throws IOException {
+
+ record TestRecord3() {
+ }
+
+ assertEquals(ReflectionRecordDecoder.OnlyFieldRecord.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecord3.class)).getClass());
+
+ JsonIterator iter = JsonIterator.parse("{ 'fieldB' : 1, 'a' : { 'fieldA' : 69 } }".replace('\'', '"'));
+ TestRecord3 record = iter.read(TestRecord3.class);
+
+ assertNotNull(record);
+ }
+
+ public void test_enum() throws IOException {
+
+ record TestRecord5(MyEnum field1) {
+
+ enum MyEnum {
+ HELLO,
+ WOW
+ }
+ }
+
+ TestRecord5 obj = JsonIterator.deserialize("{\"field1\":\"HELLO\"}", TestRecord5.class);
+ assertEquals(TestRecord5.MyEnum.HELLO, obj.field1);
+ try {
+ JsonIterator.deserialize("{\"field1\":\"HELLO1\"}", TestRecord5.class);
+ fail();
+ } catch (JsonException e) {
+ }
+ obj = JsonIterator.deserialize("{\"field1\":null}", TestRecord5.class);
+ assertNull(obj.field1);
+ obj = JsonIterator.deserialize("{\"field1\":\"WOW\"}", TestRecord5.class);
+ assertEquals(TestRecord5.MyEnum.WOW, obj.field1);
+ }
+
+ public void test_record_2_constructors_withOnlyFieldDecoder() throws IOException {
+
+ record TestRecord6(long val) {
+
+ public TestRecord6(int valInt) {
+
+ this(Long.valueOf(valInt));
+ }
+ }
+
+ assertEquals(ReflectionRecordDecoder.OnlyFieldRecord.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecord6.class)).getClass());
+
+ JsonIterator iter = JsonIterator.parse("{ 'val' : 1 }".replace('\'', '"'));
+ TestRecord6 record = iter.read(TestRecord6.class);
+
+ assertNotNull(record);
+ }
+
+ public void test_record_2_constructors_secondCtorUse_withOnlyFieldDecoder() throws IOException {
+
+ record TestRecord6(long val) {
+
+ @JsonCreator
+ public TestRecord6(@JsonProperty("valInt") int valInt) {
+
+ this(Long.valueOf(valInt));
+ }
+ }
+
+ assertEquals(ReflectionRecordDecoder.WithCtor.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecord6.class)).getClass());
+
+ JsonIterator iter = JsonIterator.parse("{ 'valInt' : 1 }".replace('\'', '"'));
+ TestRecord6 record = iter.read(TestRecord6.class);
+
+ assertNotNull(record);
+ }
+
+ public void test_record_withCtorDecoder() throws IOException {
+
+ record TestRecord2(@JsonProperty long field1) {
+
+ @JsonCreator
+ TestRecord2 {
+ }
+ }
+ assertEquals(ReflectionRecordDecoder.WithCtor.class, ReflectionDecoderFactory.create(new ClassInfo(TestRecord2.class)).getClass());
+
+ assertEquals(ReflectionDecoderFactory.create(new ClassInfo(TestRecord2.class)).getClass(), ReflectionObjectDecoder.WithCtor.class);
+
+ JsonIterator iter = JsonIterator.parse("{ 'field1' : 1 }".replace('\'', '"'));
+ TestRecord2 record = iter.read(TestRecord2.class);
+
+ assertEquals(1, record.field1);
+ }
+
+ public void test_record_constructor() throws IOException {
+ ClassDescriptor desc = ClassDescriptor.getDecodingClassDescriptor(new ClassInfo(TestRecord0.class), false);
+ assertEquals(TestRecord0.class.getConstructors()[1], desc.ctor.ctor);
+
+ }
+
+}
diff --git a/src/test/java/com/jsoniter/any/TestRecord.java b/src/test/java/com/jsoniter/any/TestRecord.java
new file mode 100644
index 00000000..4631832a
--- /dev/null
+++ b/src/test/java/com/jsoniter/any/TestRecord.java
@@ -0,0 +1,55 @@
+package com.jsoniter.any;
+import junit.framework.TestCase;
+
+import java.util.*;
+
+public class TestRecord extends TestCase {
+
+ record TestRecord1(int field1) {
+
+ }
+
+ public void test_wrap_int(){
+ Any any = Any.wrap(new TestRecord1(3));
+ assertEquals(3, any.get("field1").toInt());
+ }
+
+ record TestRecord2(int field1, String field2) {
+
+ }
+
+ public void test_iterator(){
+ Any any = Any.wrap(new TestRecord2(3,"hej"));
+ Any.EntryIterator iter = any.entries();
+ HashMap map = new HashMap();
+ while (iter.next()) {
+ if(iter.key() == "field1"){
+ assertEquals(3,iter.value().toInt());
+ }
+ if(iter.key() == "field2"){
+ assertEquals("hej",iter.value().toString());
+ }
+ }
+ }
+
+ public void test_size() {
+ Any any = Any.wrap(new TestRecord2(7,"ho"));
+ assertEquals(2, any.size());
+ }
+
+ public void test_to_string() {
+ assertEquals("{\"field1\":7,\"field2\":\"hej\"}", Any.wrap(new TestRecord2(7,"hej")).toString());
+ }
+
+ record TestRecord3(){
+
+ }
+
+ public void test_to_boolean() {
+ Any any = Any.wrap(new TestRecord3());
+ assertFalse(any.toBoolean());
+ any = Any.wrap(new TestRecord2(1,"hallo"));
+ assertTrue(any.toBoolean());
+ }
+
+}
diff --git a/src/test/java/com/jsoniter/output/TestRecord.java b/src/test/java/com/jsoniter/output/TestRecord.java
new file mode 100644
index 00000000..8a63edde
--- /dev/null
+++ b/src/test/java/com/jsoniter/output/TestRecord.java
@@ -0,0 +1,39 @@
+package com.jsoniter.output;
+
+import junit.framework.TestCase;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.*;
+
+public class TestRecord extends TestCase {
+ private ByteArrayOutputStream baos;
+ private JsonStream stream;
+
+ public void setUp() {
+ baos = new ByteArrayOutputStream();
+ stream = new JsonStream(baos, 4096);
+ }
+
+ record TestRecord1(float field1){
+
+ }
+
+ public void test_gen_record() throws IOException {
+ stream.writeVal(new TestRecord1(2.5f));
+ stream.close();
+ assertEquals("{'field1':2.5}".replace('\'', '"'), baos.toString());
+ }
+
+ record TestRecord2(){
+
+ }
+
+ public void test_empty_record() throws IOException {
+ stream.writeVal(new TestRecord2());
+ stream.close();
+ assertEquals("{}".replace('\'', '"'), baos.toString());
+ }
+
+
+
+}
diff --git a/src/test/java/com/jsoniter/suite/AllTestCases.java b/src/test/java/com/jsoniter/suite/AllTestCases.java
index d3196ed4..b5054ecf 100644
--- a/src/test/java/com/jsoniter/suite/AllTestCases.java
+++ b/src/test/java/com/jsoniter/suite/AllTestCases.java
@@ -6,6 +6,7 @@
import com.jsoniter.TestGson;
import com.jsoniter.TestNested;
import com.jsoniter.TestObject;
+import com.jsoniter.TestRecord;
import com.jsoniter.TestString;
import com.jsoniter.any.TestList;
import com.jsoniter.any.TestLong;
@@ -58,6 +59,10 @@
TestList.class,
TestAnnotationJsonObject.class,
TestLong.class,
- TestOmitValue.class})
+ TestOmitValue.class,
+ TestRecord.class,
+ com.jsoniter.output.TestRecord.class,
+ com.jsoniter.any.TestRecord.class
+})
public abstract class AllTestCases {
}