Skip to content

Records support, Java 17 and jigsaw modules #325

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 23 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
e598ce7
feat - migrated maven project to java 16 (#1)
mxyns Mar 2, 2022
fc8c4d8
Merge pull request #2 from noahra/issue/#1
mxyns Mar 2, 2022
fff39bd
test - failing test for unsupported Record (#3)
mxyns Mar 2, 2022
7fc8851
Merge pull request #4 from noahra/issue/#3
mxyns Mar 2, 2022
4766703
test - add serialization test (#7)
lnsandnkth Mar 2, 2022
0d893a4
Merge pull request #8 from noahra/issue/#7
lnsandnkth Mar 2, 2022
da40903
test - fix added assertion (#3)
lnsandnkth Mar 2, 2022
6927007
Merge pull request #10 from noahra/issue/#3
lnsandnkth Mar 2, 2022
077c4d5
feat - record support with canonical constructor (#5) (PR #13)
yuxin-miao Mar 4, 2022
149b796
feat - handle multiple record constructors by using canonical constru…
mxyns Mar 4, 2022
4b7c1f0
test - add tests for any wrap and stream (#15)
lnsandnkth Mar 4, 2022
ac59b48
Merge pull request #17 from noahra/issue/#15
lnsandnkth Mar 4, 2022
8dc32ff
test - example of use of another constructor for record decoding (#16)
mxyns Mar 6, 2022
f921396
clean - reemoved syso/sout (#19)
mxyns Mar 6, 2022
2d2ff04
clean - removed unsused imports (#19)
mxyns Mar 6, 2022
37a6755
test - add tests for getRecordCtor (#21)
Mar 6, 2022
dc3a7af
Modularized, Java17, dependency-updates.
Aug 13, 2022
e736444
trying source and target tags to try to fix jitpack.io compilation
Aug 13, 2022
bdbd720
java.lang.module.FindException: Module com.fasterxml.jackson.databind
Aug 13, 2022
6a90a5a
transitive module Javassist not found
Aug 13, 2022
1652d87
revert transitive and open for more tests
Aug 13, 2022
0451822
fixed module-info using "requires static" for optional and test-only
Aug 16, 2022
26eab37
opened module
Aug 16, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions DependencyUpdateSearch_Rules.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<ruleset xmlns="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" comparisonMethod="maven" xsi:schemaLocation="http://mojo.codehaus.org/versions-maven-plugin/rule/2.0.0 http://mojo.codehaus.org/versions-maven-plugin/xsd/rule-2.0.0.xsd">
<ignoreVersions>
<!-- Ignore Alpha's, Beta's, release candidates and milestones -->
<ignoreVersion type="regex">(?i).*Alpha(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*a(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*Beta(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*-B(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*RC(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*CR(?:-?\d+)?</ignoreVersion>
<ignoreVersion type="regex">(?i).*M(?:-?\d+)?</ignoreVersion>
</ignoreVersions>
<rules>
</rules>
</ruleset>
76 changes: 62 additions & 14 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -40,7 +40,7 @@
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
@@ -52,32 +52,38 @@
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
<version>2.13.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
<version>2.13.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.3</version>
<version>2.9.1</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>1.20</version>
<version>1.35</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.20</version>
<version>1.35</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
<scope>test</scope>
</dependency>
</dependencies>
@@ -119,17 +125,18 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<version>3.10.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<release>17</release>
<source>17</source>
<target>17</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.1</version>
<version>3.2.1</version>
<executions>
<execution>
<id>attach-sources</id>
@@ -142,15 +149,15 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
<version>3.4.0</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
<configuration>
<additionalparam>-Xdoclint:none</additionalparam>
<doclint>none</doclint>
</configuration>
</execution>
</executions>
@@ -172,7 +179,7 @@
<plugin>
<groupId>org.sonatype.plugins</groupId>
<artifactId>nexus-staging-maven-plugin</artifactId>
<version>1.6.8</version>
<version>1.6.13</version>
<extensions>true</extensions>
<configuration>
<serverId>ossrh</serverId>
@@ -194,8 +201,9 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.21.0</version>
<version>2.20</version> <!-- note: 2.20.1 to 2.22.2 crash during tests -->
<configuration>
<testFailureIgnore>true</testFailureIgnore>
<parallel>methods</parallel>
<threadCount>1</threadCount>
<reuseForks>false</reuseForks>
@@ -209,6 +217,46 @@
</includes>
</configuration>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>2.11.0</version>
<configuration>
<rulesUri>file:///${project.basedir}/DependencyUpdateSearch_Rules.xml</rulesUri>
</configuration>
<executions>
<execution>
<phase>verify</phase>
<goals>
<goal>display-dependency-updates</goal>
<goal>display-plugin-updates</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.1.0</version>
<executions>
<execution>
<id>enforce-maven</id>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<requireMavenVersion>
<version>[3.3.2,)</version>
</requireMavenVersion>
</rules>
</configuration>
</execution>
</executions>
</plugin>

</plugins>
</build>

3 changes: 3 additions & 0 deletions src/main/java/com/jsoniter/ReflectionDecoderFactory.java
Original file line number Diff line number Diff line change
@@ -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();
}
}
37 changes: 19 additions & 18 deletions src/main/java/com/jsoniter/ReflectionObjectDecoder.java
Original file line number Diff line number Diff line change
@@ -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 {
@@ -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<String, Object> extra) throws Exception {
protected void setExtra(Object obj, Map<String, Object> extra) throws Exception {
if (extra == null) {
return;
}
@@ -367,24 +368,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();
@@ -398,7 +399,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) {
@@ -409,7 +410,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++) {
@@ -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<String, Object>();
}
113 changes: 113 additions & 0 deletions src/main/java/com/jsoniter/ReflectionRecordDecoder.java
Original file line number Diff line number Diff line change
@@ -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<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;
}

}
}
14 changes: 12 additions & 2 deletions src/main/java/com/jsoniter/spi/ClassDescriptor.java
Original file line number Diff line number Diff line change
@@ -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);
@@ -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<Binding> getFields(Map<String, Type> lookup, ClassInfo classInfo, boolean includingPrivate) {
ArrayList<Binding> bindings = new ArrayList<Binding>();
for (Field field : getAllFields(classInfo.clazz)) {
@@ -432,7 +443,6 @@ public List<Binding> allDecoderBindings() {
return bindings;
}


public List<Binding> allEncoderBindings() {
ArrayList<Binding> bindings = new ArrayList<Binding>(8);
bindings.addAll(fields);
38 changes: 38 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
open module com.jsoniter {

exports com.jsoniter;

exports com.jsoniter.fuzzy;

exports com.jsoniter.static_codegen;

exports com.jsoniter.extra;

exports com.jsoniter.output;

exports com.jsoniter.annotation;

exports com.jsoniter.spi;

exports com.jsoniter.any;


/** static, because marked as optional in pom.xml*/
requires static javassist;

/** static, because marked as optional in pom.xml*/
requires static com.fasterxml.jackson.core;

/** static, because marked as optional in pom.xml*/
requires static com.fasterxml.jackson.annotation;

/** static, because marked as optional in pom.xml*/
requires static com.fasterxml.jackson.databind;

/** static, because marked as optional in pom.xml*/
requires static com.google.gson;

/** static, because only used in testing */
requires static java.desktop;

}
4 changes: 2 additions & 2 deletions src/test/java/com/jsoniter/IterImplForStreamingTest.java
Original file line number Diff line number Diff line change
@@ -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
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;
}
}
258 changes: 258 additions & 0 deletions src/test/java/com/jsoniter/TestRecord.java
Original file line number Diff line number Diff line change
@@ -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<TestRecord1> 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);

}

}
55 changes: 55 additions & 0 deletions src/test/java/com/jsoniter/any/TestRecord.java
Original file line number Diff line number Diff line change
@@ -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<String, Object> map = new HashMap<String, Object>();
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());
}

}
18 changes: 7 additions & 11 deletions src/test/java/com/jsoniter/output/TestNested.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
package com.jsoniter.output;

import java.io.*;
import java.util.*;

import com.jsoniter.annotation.JsonProperty;
import com.jsoniter.spi.JsoniterSpi;
import com.jsoniter.spi.TypeLiteral;
import junit.framework.TestCase;
import com.jsoniter.spi.*;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import junit.framework.TestCase;

public class TestNested extends TestCase {

@@ -45,7 +41,7 @@ public void test_collection_of_objects() throws IOException {
obj1.field1 = "1";
obj1.field2 = "2";
String output = JsonStream.serialize(new TypeLiteral<List<TestObject1>>() {
}, new ArrayList() {{
}, new ArrayList<>() {{
add(obj1);
}});
assertTrue(output.contains("field1"));
@@ -92,7 +88,7 @@ public void test_map_of_objects() throws IOException {
obj1.field1 = "1";
obj1.field2 = "2";
stream.writeVal(new TypeLiteral<Map<String, TestObject1>>() {
}, new HashMap() {{
}, new HashMap<>() {{
put("hello", obj1);
}});
stream.close();
39 changes: 39 additions & 0 deletions src/test/java/com/jsoniter/output/TestRecord.java
Original file line number Diff line number Diff line change
@@ -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());
}



}
7 changes: 6 additions & 1 deletion src/test/java/com/jsoniter/suite/AllTestCases.java
Original file line number Diff line number Diff line change
@@ -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 {
}