Android Gson在Kotlin data class中的使用
基本使用
data class UserBean(val name: String, val age: Int)
val json = """
{
"name":"小明",
"age":18
}
""".trimIndent()
fun main() {
val userBean = Gson().fromJson(json, UserBean::class.java)
println("姓名:${userBean.name} 年龄:${userBean.age}")
}
NEP 空指针异常问题
val json2 = """
{
"name":null,
"age":null
}
""".trimIndent()
fun main() {
val userBean = Gson().fromJson(json2, UserBean::class.java)
println("姓名:${userBean.name} 年龄:${userBean.age}")
printMsg(userBean.name)
}
fun printMsg(msg: String) {
}
原因
将printMsg() 方法翻译为Java代码:
public static final void printMsg(@NotNull String msg) {
Intrinsics.checkNotNullParameter(msg, "msg");
}
这是因为这段代码,Gson解析后,UserBean中的name字段仍然为null,而Kotlin严格区分可null和非null类型,会做类nullSafe检查,所以在调用printMsg() 方法时会抛出NPE。
nullSafe 失效问题
Gson是通过反射创建对象,然后遍历JSON中的key值和UserBean中的字段进行对应赋值的,核心代码如下:
ReflectiveTypeAdapterFactory类
@Override public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
T instance = constructor.construct();
try {
in.beginObject();
while (in.hasNext()) {
String name = in.nextName();
BoundField field = boundFields.get(name);
if (field == null || !field.deserialized) {
in.skipValue();
} else {
field.read(in, instance);
}
}
} catch (IllegalStateException e) {
throw new JsonSyntaxException(e);
} catch (IllegalAccessException e) {
throw new AssertionError(e);
}
in.endObject();
return instance;
}
ConstructorConstructor类
- newDefaultConstructor:通过反射无参构造函数创建对象。
- newDefaultImplementationConstructor:通过反射Collection和Map等集合框架类型创建对象。
- newUnsafeAllocator:通过Unsafe包创建对象,兜底方案。
UserBean翻译为Java代码:
public final class UserBean {
@NotNull
private final String name;
private final int age;
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public UserBean(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
}
由此可见,UserBean没有无参构造函数,所以这里是通过Unsafe 类创建对象的,不回调用其他的构造函数,也就绕开了Kotlin的nullSafe检查。
默认值失效问题
字段都有默认值
data class UserBean(val name: String = "未知", val age: Int = -1)
val json2 = """
{
}
""".trimIndent()
fun main() {
val userBean = Gson().fromJson(json2, UserBean::class.java)
println("姓名:${userBean.name} 年龄:${userBean.age}")
}
翻译为Java代码:
public final class UserBean {
@NotNull
private final String name;
private final int age;
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public UserBean(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
public UserBean(String var1, int var2, int var3, DefaultConstructorMarker var4) {
if ((var3 & 1) != 0) {
var1 = "未知";
}
if ((var3 & 2) != 0) {
var2 = -1;
}
this(var1, var2);
}
public UserBean() {
this((String)null, 0, 3, (DefaultConstructorMarker)null);
}
}
UserBean存在无参构造函数,所以走的是newDefaultConstructor() 方法流程。
字段部分有默认值
data class UserBean(val name: String = "未知", val age: Int)
val json2 = """
{
}
""".trimIndent()
fun main() {
val userBean = Gson().fromJson(json2, UserBean::class.java)
println("姓名:${userBean.name} 年龄:${userBean.age}")
}
翻译为Java代码:
public final class UserBean {
@NotNull
private final String name;
private final int age;
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public UserBean(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
public UserBean(String var1, int var2, int var3, DefaultConstructorMarker var4) {
if ((var3 & 1) != 0) {
var1 = "未知";
}
this(var1, var2);
}
}
UserBean类没有无参构造函数,所以这里走的Unsafe类。
解决问题
使用无参构造函数
data class UserBean(val name: String, val age: Int) {
constructor() : this("未知", -1)
}
val json = """
{
"age":18
}
""".trimIndent()
fun main() {
val userBean = Gson().fromJson(json, UserBean::class.java)
println("姓名:${userBean.name} 年龄:${userBean.age}")
}
声明为字段
本质是通过间接创建一个无参构造函数实现的。
class UserBean {
val name: String = "未知"
val age: Int = -1
}
val json = """
{
"age":18
}
""".trimIndent()
fun main() {
val userBean = Gson().fromJson(json, UserBean::class.java)
println("姓名:${userBean.name} 年龄:${userBean.age}")
}
使用moshi框架
添加依赖
implementation 'com.squareup.moshi:moshi-kotlin:1.11.0'
data class UserBean(val name: String = "未知", val age: Int)
val json = """
{
"age":18
}
""".trimIndent()
fun main() {
val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.build()
val jsonAdapter = moshi.adapter(UserBean::class.java)
val userBean = jsonAdapter.fromJson(json)
println("姓名:${userBean?.name} 年龄:${userBean?.age}")
}
|