分享一个Kotlin搭配Gson的坑
今天发现服务端返回的一个javaBean里面有三个List<T>
其实是可以合并到一个List<T>
的,所以打算合并一下,遂在javaBean中增加一个field
;
改之前
data class Example(
@SerializedName("a_list") val aList : List<String>,
@SerializedName("b_list") val bList : List<String>,
@SerializedName("c_list") val cList : List<String>
)
改之后
data class Example(
@SerializedName("a_list") val aList : List<String>,
@SerializedName("b_list") val bList : List<String>,
@SerializedName("c_list") val cList : List<String>,
val mixList: MutableList<String> = arrayListOf()
)
fun mix(){
//从服务端获取
val example = getExampleData()
example.mixList.apply {
addAll(example.aList)
addAll(example.bList)
addAll(example.cList)
}
}
fun getExampleData() : Example{
return Gson().fromJson(getData(),Example::class.java)
}
然后抛出了mixList的NullPointException
解决方案很简单,在mix
方法中给mixList
赋值
通过阅读Gson源码分析一下
构造一个Person
类试试
data class Person(
val name : String,
val age : Int,
val job : String = "coder"
)
- 首先是
fromJson
public <T> T fromJson(JsonReader reader, Type typeOfT) throws JsonIOException, JsonSyntaxException {
---
TypeToken<T> typeToken = (TypeToken<T>) TypeToken.get(typeOfT);
TypeAdapter<T> typeAdapter = getAdapter(typeToken);
T object = typeAdapter.read(reader);
return object;
---
- 可以看到这里通过
TypeAdapter
来读取字符串,
通过断点来到ReflectiveTypeAdapterFactory
的getBoundFields
方法,
private Map<String, BoundField> getBoundFields(Gson context, TypeToken<?> type, Class<?> raw) {
Map<String, BoundField> result = new LinkedHashMap<String, BoundField>();
if (raw.isInterface()) {
return result;
}
Type declaredType = type.getType();
while (raw != Object.class) {
Field[] fields = raw.getDeclaredFields();
for (Field field : fields) {
boolean serialize = excludeField(field, true);
boolean deserialize = excludeField(field, false);
if (!serialize && !deserialize) {
continue;
}
accessor.makeAccessible(field);
Type fieldType = $Gson$Types.resolve(type.getType(), raw, field.getGenericType());
List<String> fieldNames = getFieldNames(field);
BoundField previous = null;
for (int i = 0, size = fieldNames.size(); i < size; ++i) {
String name = fieldNames.get(i);
if (i != 0) serialize = false; // only serialize the default name
BoundField boundField = createBoundField(context, field, name,
TypeToken.get(fieldType), serialize, deserialize);
BoundField replaced = result.put(name, boundField);
if (previous == null) previous = replaced;
}
if (previous != null) {
throw new IllegalArgumentException(declaredType
+ " declares multiple JSON fields named " + previous.name);
}
}
type = TypeToken.get($Gson$Types.resolve(type.getType(), raw, raw.getGenericSuperclass()));
raw = type.getRawType();
}
return result;
}
- 看一下这个
raw
的来源
@Override public <T> TypeAdapter<T> create(Gson gson, final TypeToken<T> type) {
Class<? super T> raw = type.getRawType();
if (!Object.class.isAssignableFrom(raw)) {
return null; // it's a primitive!
}
ObjectConstructor<T> constructor = constructorConstructor.get(type);
return new Adapter<T>(constructor, getBoundFields(gson, type, raw));
}
也就是我们传进来要转换的结果class
,gson通过默认构造函数构造了一个Person
对象,通过遍历field
,并进行一一匹配填充,而没有读取到的初始值为空,所以我们在data class
中赋的值没有意义了~
- 看一下Kotlin的构造器问题
也就是说,我们赋的默认值失去了意义~
其实也没啥,就是记录一下