Skip to content

Commit 814b1c3

Browse files
committed
fix kotlin serialize JSONField annotation not work, for issue #3287
1 parent 39619cd commit 814b1c3

File tree

4 files changed

+192
-131
lines changed

4 files changed

+192
-131
lines changed

core/src/main/java/com/alibaba/fastjson2/util/KotlinUtils.java

+147-1
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,25 @@
88
import kotlin.reflect.KParameter;
99

1010
import java.lang.reflect.Constructor;
11-
import java.util.List;
11+
import java.lang.reflect.Method;
12+
import java.util.*;
1213

1314
/**
1415
* @author kraity
1516
* @since 2.0.39
1617
*/
1718
public class KotlinUtils {
1819
public static final int STATE;
20+
private static volatile Class kotlin_metadata;
21+
private static volatile boolean kotlin_metadata_error;
22+
private static volatile boolean kotlin_class_klass_error;
23+
private static volatile Constructor kotlin_kclass_constructor;
24+
private static volatile Method kotlin_kclass_getConstructors;
25+
private static volatile Method kotlin_kfunction_getParameters;
26+
private static volatile Method kotlin_kparameter_getName;
27+
private static volatile boolean kotlin_error;
28+
private static volatile Map<Class, String[]> kotlinIgnores;
29+
private static volatile boolean kotlinIgnores_error;
1930

2031
static {
2132
int state = 0;
@@ -95,4 +106,139 @@ public static void getConstructor(Class<?> clazz, BeanInfo beanInfo) {
95106

96107
beanInfo.creatorConstructor = creatorConstructor;
97108
}
109+
110+
public static boolean isKotlin(Class clazz) {
111+
if (kotlin_metadata == null && !kotlin_metadata_error) {
112+
try {
113+
kotlin_metadata = Class.forName("kotlin.Metadata");
114+
} catch (Throwable e) {
115+
kotlin_metadata_error = true;
116+
}
117+
}
118+
return kotlin_metadata != null && clazz.isAnnotationPresent(kotlin_metadata);
119+
}
120+
121+
public static Constructor getKotlinConstructor(Constructor[] constructors) {
122+
return getKotlinConstructor(constructors, null);
123+
}
124+
125+
public static Constructor getKotlinConstructor(Constructor[] constructors, String[] paramNames) {
126+
Constructor creatorConstructor = null;
127+
for (Constructor<?> constructor : constructors) {
128+
Class<?>[] parameterTypes = constructor.getParameterTypes();
129+
if (paramNames != null && parameterTypes.length != paramNames.length) {
130+
continue;
131+
}
132+
// String equals to Class will always return false !
133+
if (parameterTypes.length > 0 && "kotlin.jvm.internal.DefaultConstructorMarker".equals(parameterTypes[parameterTypes.length - 1].getName())) {
134+
continue;
135+
}
136+
if (creatorConstructor != null && creatorConstructor.getParameterTypes().length >= parameterTypes.length) {
137+
continue;
138+
}
139+
creatorConstructor = constructor;
140+
}
141+
return creatorConstructor;
142+
}
143+
144+
public static String[] getKoltinConstructorParameters(Class clazz) {
145+
if (kotlin_kclass_constructor == null && !kotlin_class_klass_error) {
146+
try {
147+
Class class_kotlin_kclass = Class.forName("kotlin.reflect.jvm.internal.KClassImpl");
148+
kotlin_kclass_constructor = class_kotlin_kclass.getConstructor(Class.class);
149+
} catch (Throwable e) {
150+
kotlin_class_klass_error = true;
151+
}
152+
}
153+
if (kotlin_kclass_constructor == null) {
154+
return null;
155+
}
156+
157+
if (kotlin_kclass_getConstructors == null && !kotlin_class_klass_error) {
158+
try {
159+
Class class_kotlin_kclass = Class.forName("kotlin.reflect.jvm.internal.KClassImpl");
160+
kotlin_kclass_getConstructors = class_kotlin_kclass.getMethod("getConstructors");
161+
} catch (Throwable e) {
162+
kotlin_class_klass_error = true;
163+
}
164+
}
165+
166+
if (kotlin_kfunction_getParameters == null && !kotlin_class_klass_error) {
167+
try {
168+
Class class_kotlin_kfunction = Class.forName("kotlin.reflect.KFunction");
169+
kotlin_kfunction_getParameters = class_kotlin_kfunction.getMethod("getParameters");
170+
} catch (Throwable e) {
171+
kotlin_class_klass_error = true;
172+
}
173+
}
174+
175+
if (kotlin_kparameter_getName == null && !kotlin_class_klass_error) {
176+
try {
177+
Class class_kotlinn_kparameter = Class.forName("kotlin.reflect.KParameter");
178+
kotlin_kparameter_getName = class_kotlinn_kparameter.getMethod("getName");
179+
} catch (Throwable e) {
180+
kotlin_class_klass_error = true;
181+
}
182+
}
183+
184+
if (kotlin_error) {
185+
return null;
186+
}
187+
188+
try {
189+
Object constructor = null;
190+
Object kclassImpl = kotlin_kclass_constructor.newInstance(clazz);
191+
Iterable it = (Iterable) kotlin_kclass_getConstructors.invoke(kclassImpl);
192+
for (Iterator iterator = it.iterator(); iterator.hasNext();) {
193+
Object item = iterator.next();
194+
List parameters = (List) kotlin_kfunction_getParameters.invoke(item);
195+
if (constructor != null && parameters.size() == 0) {
196+
continue;
197+
}
198+
constructor = item;
199+
}
200+
201+
if (constructor == null) {
202+
return null;
203+
}
204+
205+
List parameters = (List) kotlin_kfunction_getParameters.invoke(constructor);
206+
String[] names = new String[parameters.size()];
207+
for (int i = 0; i < parameters.size(); i++) {
208+
Object param = parameters.get(i);
209+
names[i] = (String) kotlin_kparameter_getName.invoke(param);
210+
}
211+
return names;
212+
} catch (Throwable e) {
213+
e.printStackTrace();
214+
kotlin_error = true;
215+
}
216+
return null;
217+
}
218+
219+
public static boolean isKotlinIgnore(Class clazz, String methodName) {
220+
if (kotlinIgnores == null && !kotlinIgnores_error) {
221+
try {
222+
Map<Class, String[]> map = new HashMap<>();
223+
Class charRangeClass = Class.forName("kotlin.ranges.CharRange");
224+
map.put(charRangeClass, new String[]{"getEndInclusive", "isEmpty"});
225+
Class intRangeClass = Class.forName("kotlin.ranges.IntRange");
226+
map.put(intRangeClass, new String[]{"getEndInclusive", "isEmpty"});
227+
Class longRangeClass = Class.forName("kotlin.ranges.LongRange");
228+
map.put(longRangeClass, new String[]{"getEndInclusive", "isEmpty"});
229+
Class floatRangeClass = Class.forName("kotlin.ranges.ClosedFloatRange");
230+
map.put(floatRangeClass, new String[]{"getEndInclusive", "isEmpty"});
231+
Class doubleRangeClass = Class.forName("kotlin.ranges.ClosedDoubleRange");
232+
map.put(doubleRangeClass, new String[]{"getEndInclusive", "isEmpty"});
233+
kotlinIgnores = map;
234+
} catch (Throwable error) {
235+
kotlinIgnores_error = true;
236+
}
237+
}
238+
if (kotlinIgnores == null) {
239+
return false;
240+
}
241+
String[] ignores = kotlinIgnores.get(clazz);
242+
return ignores != null && Arrays.binarySearch(ignores, methodName) >= 0;
243+
}
98244
}

core/src/main/java/com/alibaba/fastjson2/writer/ObjectWriterBaseModule.java

+20
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,26 @@ public void getFieldInfo(BeanInfo beanInfo, FieldInfo fieldInfo, Class objectCla
303303

304304
JSONField jsonField = null;
305305
Annotation[] annotations = getAnnotations(field);
306+
if (annotations.length == 0 && KotlinUtils.isKotlin(objectClass)) {
307+
annotations = getAnnotations(field.getType());
308+
Constructor kotlinConstructor = KotlinUtils.getKotlinConstructor(getConstructor(objectClass));
309+
if (kotlinConstructor != null) {
310+
String[] paramNames = KotlinUtils.getKoltinConstructorParameters(objectClass);
311+
for (int i = 0; i < paramNames.length; i++) {
312+
if (paramNames[i].equals(field.getName())) {
313+
annotations = kotlinConstructor.getParameterAnnotations()[i];
314+
break;
315+
}
316+
}
317+
if (fieldInfo.ignore) {
318+
for (Annotation annotation : annotations) {
319+
if (annotation.annotationType() == JSONField.class) {
320+
fieldInfo.ignore = !((JSONField) annotation).serialize();
321+
}
322+
}
323+
}
324+
}
325+
}
306326
for (Annotation annotation : annotations) {
307327
Class<? extends Annotation> annotationType = annotation.annotationType();
308328
if (jsonField == null) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.alibaba.fastjson2.issues_3300
2+
3+
import com.alibaba.fastjson2.JSON
4+
import com.alibaba.fastjson2.annotation.JSONField
5+
import org.junit.jupiter.api.Assertions
6+
import org.junit.jupiter.api.Test
7+
8+
class Issue3287 {
9+
@Test
10+
fun test() {
11+
val bean = Bean(1, 2);
12+
Assertions.assertEquals("{\"a\":1,\"b\":2}", JSON.toJSONString(bean))
13+
}
14+
15+
data class Bean(
16+
@JSONField(serialize = true, deserialize = true)
17+
private val a: Int,
18+
val b: Int
19+
)
20+
}

fastjson1-compatible/src/main/java/com/alibaba/fastjson/util/TypeUtils.java

+5-130
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import com.alibaba.fastjson.serializer.SerializerFeature;
2727
import com.alibaba.fastjson2.JSONFactory;
2828
import com.alibaba.fastjson2.reader.ObjectReader;
29+
import com.alibaba.fastjson2.util.KotlinUtils;
2930

3031
import java.io.InputStream;
3132
import java.io.Reader;
@@ -47,17 +48,6 @@ public class TypeUtils {
4748
public static boolean compatibleWithJavaBean;
4849
public static boolean compatibleWithFieldName;
4950

50-
private static volatile Class kotlin_metadata;
51-
private static volatile boolean kotlin_metadata_error;
52-
private static volatile boolean kotlin_class_klass_error;
53-
private static volatile Constructor kotlin_kclass_constructor;
54-
private static volatile Method kotlin_kclass_getConstructors;
55-
private static volatile Method kotlin_kfunction_getParameters;
56-
private static volatile Method kotlin_kparameter_getName;
57-
private static volatile boolean kotlin_error;
58-
private static volatile Map<Class, String[]> kotlinIgnores;
59-
private static volatile boolean kotlinIgnores_error;
60-
6151
private static boolean transientClassInited;
6252
private static Class<? extends Annotation> transientClass;
6353

@@ -1123,138 +1113,23 @@ static void setAccessible(AccessibleObject obj) {
11231113
}
11241114

11251115
public static boolean isKotlin(Class clazz) {
1126-
if (kotlin_metadata == null && !kotlin_metadata_error) {
1127-
try {
1128-
kotlin_metadata = Class.forName("kotlin.Metadata");
1129-
} catch (Throwable e) {
1130-
kotlin_metadata_error = true;
1131-
}
1132-
}
1133-
return kotlin_metadata != null && clazz.isAnnotationPresent(kotlin_metadata);
1116+
return KotlinUtils.isKotlin(clazz);
11341117
}
11351118

11361119
public static Constructor getKotlinConstructor(Constructor[] constructors) {
11371120
return getKotlinConstructor(constructors, null);
11381121
}
11391122

11401123
public static Constructor getKotlinConstructor(Constructor[] constructors, String[] paramNames) {
1141-
Constructor creatorConstructor = null;
1142-
for (Constructor<?> constructor : constructors) {
1143-
Class<?>[] parameterTypes = constructor.getParameterTypes();
1144-
if (paramNames != null && parameterTypes.length != paramNames.length) {
1145-
continue;
1146-
}
1147-
// String equals to Class will always return false !
1148-
if (parameterTypes.length > 0 && "kotlin.jvm.internal.DefaultConstructorMarker".equals(parameterTypes[parameterTypes.length - 1].getName())) {
1149-
continue;
1150-
}
1151-
if (creatorConstructor != null && creatorConstructor.getParameterTypes().length >= parameterTypes.length) {
1152-
continue;
1153-
}
1154-
creatorConstructor = constructor;
1155-
}
1156-
return creatorConstructor;
1124+
return KotlinUtils.getKotlinConstructor(constructors, paramNames);
11571125
}
11581126

11591127
public static String[] getKoltinConstructorParameters(Class clazz) {
1160-
if (kotlin_kclass_constructor == null && !kotlin_class_klass_error) {
1161-
try {
1162-
Class class_kotlin_kclass = Class.forName("kotlin.reflect.jvm.internal.KClassImpl");
1163-
kotlin_kclass_constructor = class_kotlin_kclass.getConstructor(Class.class);
1164-
} catch (Throwable e) {
1165-
kotlin_class_klass_error = true;
1166-
}
1167-
}
1168-
if (kotlin_kclass_constructor == null) {
1169-
return null;
1170-
}
1171-
1172-
if (kotlin_kclass_getConstructors == null && !kotlin_class_klass_error) {
1173-
try {
1174-
Class class_kotlin_kclass = Class.forName("kotlin.reflect.jvm.internal.KClassImpl");
1175-
kotlin_kclass_getConstructors = class_kotlin_kclass.getMethod("getConstructors");
1176-
} catch (Throwable e) {
1177-
kotlin_class_klass_error = true;
1178-
}
1179-
}
1180-
1181-
if (kotlin_kfunction_getParameters == null && !kotlin_class_klass_error) {
1182-
try {
1183-
Class class_kotlin_kfunction = Class.forName("kotlin.reflect.KFunction");
1184-
kotlin_kfunction_getParameters = class_kotlin_kfunction.getMethod("getParameters");
1185-
} catch (Throwable e) {
1186-
kotlin_class_klass_error = true;
1187-
}
1188-
}
1189-
1190-
if (kotlin_kparameter_getName == null && !kotlin_class_klass_error) {
1191-
try {
1192-
Class class_kotlinn_kparameter = Class.forName("kotlin.reflect.KParameter");
1193-
kotlin_kparameter_getName = class_kotlinn_kparameter.getMethod("getName");
1194-
} catch (Throwable e) {
1195-
kotlin_class_klass_error = true;
1196-
}
1197-
}
1198-
1199-
if (kotlin_error) {
1200-
return null;
1201-
}
1202-
1203-
try {
1204-
Object constructor = null;
1205-
Object kclassImpl = kotlin_kclass_constructor.newInstance(clazz);
1206-
Iterable it = (Iterable) kotlin_kclass_getConstructors.invoke(kclassImpl);
1207-
for (Iterator iterator = it.iterator(); iterator.hasNext();) {
1208-
Object item = iterator.next();
1209-
List parameters = (List) kotlin_kfunction_getParameters.invoke(item);
1210-
if (constructor != null && parameters.size() == 0) {
1211-
continue;
1212-
}
1213-
constructor = item;
1214-
}
1215-
1216-
if (constructor == null) {
1217-
return null;
1218-
}
1219-
1220-
List parameters = (List) kotlin_kfunction_getParameters.invoke(constructor);
1221-
String[] names = new String[parameters.size()];
1222-
for (int i = 0; i < parameters.size(); i++) {
1223-
Object param = parameters.get(i);
1224-
names[i] = (String) kotlin_kparameter_getName.invoke(param);
1225-
}
1226-
return names;
1227-
} catch (Throwable e) {
1228-
e.printStackTrace();
1229-
kotlin_error = true;
1230-
}
1231-
return null;
1128+
return KotlinUtils.getKoltinConstructorParameters(clazz);
12321129
}
12331130

12341131
static boolean isKotlinIgnore(Class clazz, String methodName) {
1235-
if (kotlinIgnores == null && !kotlinIgnores_error) {
1236-
try {
1237-
Map<Class, String[]> map = new HashMap<>();
1238-
Class charRangeClass = Class.forName("kotlin.ranges.CharRange");
1239-
map.put(charRangeClass, new String[]{"getEndInclusive", "isEmpty"});
1240-
Class intRangeClass = Class.forName("kotlin.ranges.IntRange");
1241-
map.put(intRangeClass, new String[]{"getEndInclusive", "isEmpty"});
1242-
Class longRangeClass = Class.forName("kotlin.ranges.LongRange");
1243-
map.put(longRangeClass, new String[]{"getEndInclusive", "isEmpty"});
1244-
Class floatRangeClass = Class.forName("kotlin.ranges.ClosedFloatRange");
1245-
map.put(floatRangeClass, new String[]{"getEndInclusive", "isEmpty"});
1246-
Class doubleRangeClass = Class.forName("kotlin.ranges.ClosedDoubleRange");
1247-
map.put(doubleRangeClass, new String[]{"getEndInclusive", "isEmpty"});
1248-
kotlinIgnores = map;
1249-
} catch (Throwable error) {
1250-
kotlinIgnores_error = true;
1251-
}
1252-
}
1253-
if (kotlinIgnores == null) {
1254-
return false;
1255-
}
1256-
String[] ignores = kotlinIgnores.get(clazz);
1257-
return ignores != null && Arrays.binarySearch(ignores, methodName) >= 0;
1132+
return KotlinUtils.isKotlinIgnore(clazz, methodName);
12581133
}
12591134

12601135
private static boolean isJSONTypeIgnore(Class<?> clazz, String propertyName) {

0 commit comments

Comments
 (0)