Skip to content
This repository has been archived by the owner on Oct 23, 2024. It is now read-only.

Commit

Permalink
improved support kotlin data class. #1374
Browse files Browse the repository at this point in the history
  • Loading branch information
wenshao committed Aug 6, 2017
1 parent 1de9938 commit 90e821b
Show file tree
Hide file tree
Showing 12 changed files with 243 additions and 8 deletions.
5 changes: 3 additions & 2 deletions src/main/java/com/alibaba/fastjson/parser/JavaBeanInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -388,10 +388,11 @@ public static JavaBeanInfo build(Class<?> clazz, //
}
} else if (!isInterfaceOrAbstract){
boolean kotlin = TypeUtils.isKotlin(clazz);
if (kotlin && constructors.length == 1) {
if (kotlin && constructors.length > 0) {
String[] parameters = TypeUtils.getKoltinConstructorParameters(clazz);
if (parameters != null) {
creatorConstructor = constructors[0];
creatorConstructor = constructors[constructors.length - 1];
creatorConstructor.setAccessible(true);
TypeUtils.setAccessible(clazz, creatorConstructor, classModifiers);

Class<?>[] parameterTypes = creatorConstructor.getParameterTypes();
Expand Down
56 changes: 50 additions & 6 deletions src/main/java/com/alibaba/fastjson/util/TypeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ public class TypeUtils {

private static volatile boolean kotlin_error;

private static volatile Map<Class, String[]> kotlinIgnores;
private static volatile boolean kotlinIgnores_error;

public static boolean isKotlin(Class clazz) {
if (kotlin_metadata == null && !kotlin_metadata_error) {
try {
Expand All @@ -73,6 +76,44 @@ public static boolean isKotlin(Class clazz) {
return clazz.isAnnotationPresent(kotlin_metadata);
}

private static boolean isKotlinIgnore(Class clazz, String methodName) {
if (kotlinIgnores == null && !kotlinIgnores_error) {
try {
Map<Class, String[]> map = new HashMap<Class, String[]>();

Class charRangeClass = Class.forName("kotlin.ranges.CharRange");
map.put(charRangeClass, new String[]{"getEndInclusive","isEmpty"});

Class intRangeClass = Class.forName("kotlin.ranges.IntRange");
map.put(intRangeClass, new String[]{"getEndInclusive","isEmpty"});

Class longRangeClass = Class.forName("kotlin.ranges.LongRange");
map.put(longRangeClass, new String[]{"getEndInclusive", "isEmpty"});

Class floatRangeClass = Class.forName("kotlin.ranges.ClosedFloatRange");
map.put(floatRangeClass, new String[]{"getEndInclusive","isEmpty"});

Class doubleRangeClass = Class.forName("kotlin.ranges.ClosedDoubleRange");
map.put(doubleRangeClass, new String[]{"getEndInclusive","isEmpty"});

kotlinIgnores = map;
} catch (Throwable error) {
kotlinIgnores_error = true;
}
}

if (kotlinIgnores == null) {
return false;
}

String[] ignores = kotlinIgnores.get(clazz);
if (ignores == null) {
return false;
}

return Arrays.binarySearch(ignores, methodName) >= 0;
}

public static String[] getKoltinConstructorParameters(Class clazz) {
if (kotlin_kclass_constructor == null && !kotlin_class_klass_error) {
try {
Expand All @@ -99,13 +140,12 @@ public static String[] getKoltinConstructorParameters(Class clazz) {
}

try {
Object constructor = null;
Object kclassImpl = kotlin_kclass_constructor.newInstance(clazz);
Iterable it = (Iterable) kotlin_kclass_getConstructors.invoke(kclassImpl);
Iterator iterator = it.iterator();
if (!iterator.hasNext()) {
return null;
for (Iterator iterator = it.iterator();iterator.hasNext();iterator.hasNext()) {
constructor = iterator.next();
}
Object constructor = iterator.next();

List parameters = (List) kotlin_kfunction_getParameters.invoke(constructor);
String[] names = new String[parameters.size()];
Expand Down Expand Up @@ -915,10 +955,10 @@ public static List<FieldInfo> computeGetters(Class<?> clazz, //
Map<String, FieldInfo> fieldInfoMap = new LinkedHashMap<String, FieldInfo>();
Map<Class<?>, Field[]> classFieldCache = new HashMap<Class<?>, Field[]>();

boolean kotlin = TypeUtils.isKotlin(clazz);

Field[] declaredFields = clazz.getDeclaredFields();
if (!fieldOnly) {
boolean kotlin = TypeUtils.isKotlin(clazz);

List<Method> methodList = new ArrayList<Method>();

for (Class cls = clazz; cls != null && cls != Object.class; cls = cls.getSuperclass()) {
Expand Down Expand Up @@ -966,6 +1006,10 @@ public static List<FieldInfo> computeGetters(Class<?> clazz, //
annotation = getSupperMethodAnnotation(clazz, method);
}

if (kotlin && isKotlinIgnore(clazz, methodName)) {
continue;
}

if (annotation == null && kotlin) {
if (constructors == null) {
constructors = clazz.getDeclaredConstructors();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.alibaba.json.bvt.kotlin;

import com.alibaba.fastjson.JSON;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;

/**
* Created by wenshao on 06/08/2017.
*/
public class ClassWithPairMixedTypesTest extends TestCase {
public void test_user() throws Exception {
ExtClassLoader classLoader = new ExtClassLoader();
Class clazz = classLoader.loadClass("ClassWithPairMixedTypes");

String json = "{\"person\":{\"first\":\"wenshao\",\"second\":99}}";
Object obj = JSON.parseObject(json, clazz);
assertEquals("{\"person\":{\"first\":\"wenshao\",\"second\":99}}", JSON.toJSONString(obj));
}

public static class ExtClassLoader extends ClassLoader {

public ExtClassLoader() throws IOException {
super(Thread.currentThread().getContextClassLoader());

{
byte[] bytes;
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("kotlin/ClassWithPairMixedTypes.clazz");
bytes = IOUtils.toByteArray(is);
is.close();

super.defineClass("ClassWithPairMixedTypes", bytes, 0, bytes.length);
}
}
}
}
38 changes: 38 additions & 0 deletions src/test/java/com/alibaba/json/bvt/kotlin/ClassWithPairTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.alibaba.json.bvt.kotlin;

import com.alibaba.fastjson.JSON;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;

/**
* Created by wenshao on 06/08/2017.
*/
public class ClassWithPairTest extends TestCase {
public void test_user() throws Exception {
ExtClassLoader classLoader = new ExtClassLoader();
Class clazz = classLoader.loadClass("ClassWithPair");

String json = "{\"name\":{\"first\":\"shaojin\",\"second\":\"wen\"},\"age\":99}";
Object obj = JSON.parseObject(json, clazz);
assertEquals("{\"age\":99,\"name\":{\"first\":\"shaojin\",\"second\":\"wen\"}}", JSON.toJSONString(obj));
}

public static class ExtClassLoader extends ClassLoader {

public ExtClassLoader() throws IOException {
super(Thread.currentThread().getContextClassLoader());

{
byte[] bytes;
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("kotlin/ClassWithPair.clazz");
bytes = IOUtils.toByteArray(is);
is.close();

super.defineClass("ClassWithPair", bytes, 0, bytes.length);
}
}
}
}
38 changes: 38 additions & 0 deletions src/test/java/com/alibaba/json/bvt/kotlin/ClassWithRangesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.alibaba.json.bvt.kotlin;

import com.alibaba.fastjson.JSON;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;

/**
* Created by wenshao on 06/08/2017.
*/
public class ClassWithRangesTest extends TestCase {
public void test_user() throws Exception {
ExtClassLoader classLoader = new ExtClassLoader();
Class clazz = classLoader.loadClass("ClassWithRanges");

String json = "{\"ages\":{\"start\":18,\"end\":40},\"distance\":{\"start\":5,\"end\":50}}";
Object obj = JSON.parseObject(json, clazz);
assertEquals("{\"ages\":{\"first\":18,\"last\":0,\"start\":18,\"step\":1},\"distance\":{\"first\":5,\"last\":0,\"start\":5,\"step\":1}}", JSON.toJSONString(obj));
}

public static class ExtClassLoader extends ClassLoader {

public ExtClassLoader() throws IOException {
super(Thread.currentThread().getContextClassLoader());

{
byte[] bytes;
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("kotlin/ClassWithRanges.clazz");
bytes = IOUtils.toByteArray(is);
is.close();

super.defineClass("ClassWithRanges", bytes, 0, bytes.length);
}
}
}
}
38 changes: 38 additions & 0 deletions src/test/java/com/alibaba/json/bvt/kotlin/ClassWithTripleTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.alibaba.json.bvt.kotlin;

import com.alibaba.fastjson.JSON;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;

/**
* Created by wenshao on 06/08/2017.
*/
public class ClassWithTripleTest extends TestCase {
public void test_user() throws Exception {
ExtClassLoader classLoader = new ExtClassLoader();
Class clazz = classLoader.loadClass("ClassWithTriple");

String json = "{\"name\":{\"first\":\"wen\",\"second\":\"shaojin\",\"third\":99}}";
Object obj = JSON.parseObject(json, clazz);
assertEquals("{\"age\":0,\"name\":{\"first\":\"wen\",\"second\":\"shaojin\",\"third\":\"99\"}}", JSON.toJSONString(obj));
}

public static class ExtClassLoader extends ClassLoader {

public ExtClassLoader() throws IOException {
super(Thread.currentThread().getContextClassLoader());

{
byte[] bytes;
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("kotlin/ClassWithTriple.clazz");
bytes = IOUtils.toByteArray(is);
is.close();

super.defineClass("ClassWithTriple", bytes, 0, bytes.length);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.alibaba.json.bvt.kotlin;

import com.alibaba.fastjson.JSON;
import junit.framework.TestCase;
import org.apache.commons.io.IOUtils;

import java.io.IOException;
import java.io.InputStream;

/**
* Created by wenshao on 06/08/2017.
*/
public class Class_WithPrimaryAndSecondaryConstructorTest extends TestCase {
public void test_user() throws Exception {
ExtClassLoader classLoader = new ExtClassLoader();
Class clazz = classLoader.loadClass("Class_WithPrimaryAndSecondaryConstructor");

String json = "{\"name\":\"John Smith\",\"age\":30}";
Object obj = JSON.parseObject(json, clazz);
assertEquals("{\"age\":30,\"name\":\"John Smith\"}", JSON.toJSONString(obj));
}

public static class ExtClassLoader extends ClassLoader {

public ExtClassLoader() throws IOException {
super(Thread.currentThread().getContextClassLoader());

{
byte[] bytes;
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("kotlin/Class_WithPrimaryAndSecondaryConstructor.clazz");
bytes = IOUtils.toByteArray(is);
is.close();

super.defineClass("Class_WithPrimaryAndSecondaryConstructor", bytes, 0, bytes.length);
}
}
}
}
Binary file added src/test/resources/kotlin/ClassWithPair.clazz
Binary file not shown.
Binary file not shown.
Binary file added src/test/resources/kotlin/ClassWithRanges.clazz
Binary file not shown.
Binary file added src/test/resources/kotlin/ClassWithTriple.clazz
Binary file not shown.
Binary file not shown.

0 comments on commit 90e821b

Please sign in to comment.