fastjson kotlin中的默认值支持有问题

cgyqldqp  于 2021-11-27  发布在  Java
关注(0)|答案(12)|浏览(594)

假设有如下代码
data class Data(
val a: Int,
val b: Int=20
)
其中参数b有个默认值,如果我们定义一个json字符串,假设如下
val json="""{"a":45,"b":60}"""
那么,如果进行反序列化,时成功的
JSON.parseObject(json,Data::class.java)
这行代码能得到正确的结果,b的值是60
但是,假设我们的json字符串如下
val json="""{"a":45}"""
注意,和上面相比,去掉了b这个参数,那么,在没有提供b参数的情况下,我们希望b采用的是默认值,20,但是实际情况是,b的值为0,

如果b的类型比较特殊,比如是时间类型Date,那么就会直接抛出异常了,比如以下代码
data class Data(
val a: Int,
val b: Date=Date()
)
val json="""{"a":45}"""
val value = JSON.parseObject(json,Data::class.java)
如果调用,则直接抛出create instance error

Exception in thread "main" com.alibaba.fastjson.JSONException: create instance error, [Ljava.lang.String;@792b749c, public aaaa.Data(int,java.util.Date)
at ...........

iih3973s

iih3973s1#

@lee87902407@wenshao 这个问题在fastJSON和GSON里都存在,确实应该提供这种策略,比如{"a":45, "b":null},这时候,我可能想忽略掉b的null值,因为我认为null是无实际意义的,解析的时候,应该提供忽略null的这种策略,而且能够使用编写类的时候提供的默认值,而不是让fastjson赋一个默认值。当然,有的人会把null当成一种特定意义的情况,但是不建议这么用,因为对于kotlin的NULL safe和@NotNull造成破坏。

umuewwlo

umuewwlo2#

这个问题好像卡很久了,目前就是所有在data class构造函数创建参数都必须传入,如果需要可选的话只能放到body里面, @wenshao 大佬这个什么时候能修复呀 等好久了的

wbrvyc0a

wbrvyc0a3#

@fangzhengjin 试一试加上@JVMOverloads

eivgtgni

eivgtgni4#

@leleliu008 之前都试过了...目前暂时替代方法只能是将所有data class主构造函数中的字段都设置默认值,如果用到jpa的话还需要自己引入noarg插件

data class Test(
    var field1: String? = null,
    var field2: String? = "",
    var field3: Int = 0
){
    var otherField: String
}
wlzqhblo

wlzqhblo5#

@fangzhengjin 您上面的例子有一处错误,应该是如下的写法:

data class Test(
    var field1: String? = null,
    var field2: String? = "",
    var field3: Int = 0
) {
    lateinit var otherField: String
}

我好奇的是,您为什么要这么写?虽然Kotlin语法允许这么写。为啥不写成如下:

data class Test(
    var field1: String? = null,
    var field2: String? = "",
    var field3: Int = 0,
    var otherField: String = ""
)

通常,我们的实体类不是自己手写的,而是使用工具生成的,因为实体类都是根据数据库或者定义好的协议自动生成的,往往生成的就是我的第二种写法,为了保持住KotlinNullSafty这个特性,每个属性都会提供一个默认值,这是因为当Kotlin的每个属性都有默认值的时候,编译时候就会自动生成无参数的构造方法,而fastJSONGSON在反序列化的时候,它要构造示例出来的时候,首先检测有没有默认的构造方法,有的话就会反射那个无参数的构造方法,然后才是一个属性一个属性的反射设置的。

另外:

data class Test(
    var field1: String? = null,
    var field2: String? = "",
    var field3: Int = 0
) {
    lateinit var otherField: String
}

翻译成Java就是如下的代码:

public final class Test {
   @NotNull
   public String otherField;
   @Nullable
   private String field1;
   @Nullable
   private String field2;
   private int field3;

   public Foo(@Nullable String field1, @Nullable String field2, int field3) {
      this.field1 = field1;
      this.field2 = field2;
      this.field3 = field3;
   }
}

其他的gettersetter方法我没有写,otherField这个属性实际上默认值就是null,而在kotlin里你的写法似乎想让它不是null,这就矛盾了。

rqdpfwrv

rqdpfwrv6#

@leleliu008 otherField这个字段确实是表达有误,那我们可以只看构造函数部分

data class Test(
    var field1: String,
    var field2: String? = "field2Content",
    var field3: Int = 0
)

如果field1字段没有设置默认值,如果我使用下面的json串进行反序列化时,将会出现无法示例化的错误,即构造器要求提供三个参数constructor(String, String, Int)

{
    "field1": "field1Content"
}

如果想使用FastJson反序列化只能

  1. 所有在构造器中声明的变量全部设置默认值(包括null),这将违反了使用data class的初衷,即必要参数不能为空
data class Test(
    var field1: String = null,
    var field2: String? = "field2Content",
    var field3: Int = 0
)
  1. json串提供所有构造器中的属性
{
    "field1": "field1Content",
    "field1": "field2Content",
    "field3": 0
}

但是在使用jackjson时, 对kotlin提供了良好的互操作, 不会出现上述问题

7xzttuei

7xzttuei7#

@fangzhengjin 这是因为fastJSON在没有默认的构造函数的时候,去尝试反射其他的构造函数导致的,所以,如果你想使用fastJSON,还想不出错,最好的实践就是全部都提供默认的值,这就就不会有任何问题。

我比较好奇的是,为啥不提供默认值?这个也不用您手写,都是工具生成的。

至于为啥fastJSON这么设计,可能他是出于效率的考虑,也或者是它压根儿就没有考虑到,总之,这个我们不得而知。如果您想使用这个库,就得接受这个事实。毕竟,fastJSON引以为傲的是它的反序列化速度,多加一些功能进去就意味着很可能会降低速度,所以,他会作出一些取舍。

您可以考虑使用https://github.com/square/moshi,这个库不会出现此问题。对Kotlin很友好,但是这些库都存在一个问题,就是在反序列化的时候,不能忽略null,你非得修改他们的源代码不可,不过修改起来也很容易的。

vfwfrxfs

vfwfrxfs8#

@leleliu008
1、因为我们开发的项目中普遍使用JPA, 所以实体类是手工创建的, 数据库是自动创建的,所以必要参数不提供默认值,因为不允许为空.
2、我觉得应该是设计没有考虑到,我看是用java反射直接创建的,获取到的构造器可能不是预期的,毕竟有其他先例在前,实现肯定是没问题的

41zrol4v

41zrol4v9#

@fangzhengjin
1、你们使用的是Spring Data JPA,它根据实体类自动创建数据库的表,这个我明白,但是,难到不事先进行数据库的设计吗?一般的数据库设计软件都有导出各种语言和各种结构化数据的插件。我指的是这个,当然,以前不使用自动创建数据库表的时候,连接上数据库也能直接从数据库表生成实体类。
2、所以必要参数不提供默认值,因为不允许为空,这句话没理解,我觉得提供默认值和null不冲突的嘛,现在我不明白的是,您纠结在构造函数上还是null 的问题上,实际上,您提供默认值最好不要是null,就是您的代码里压根儿不要使用null,字符串儿使用"",数字使用-1、0,之类的不就可以了嘛

new9mtju

new9mtju10#

请问这个问题什么时候能解决?

data class Comment (
    val content: String = "",
    val gmtCreate : Date = Date()
)

当json为{content: "test"}时,在Android中会直接报异常崩溃create instance error(与issue中的异常一致),gmtCreate如果设为Date?(可为null的类型),就可以运行,但是要在程序中判断是否为空,并且gmtCreate的默认值也失去了作用。
希望尽快解决!谢谢

k2fxgqgv

k2fxgqgv11#

@15050050972

  1. {content: 'test'}并不是标准的JSON,标准的JSON字符串应该用双引号,你用的单引号。
  2. 你说的报异常,是啥异常,问题描述的不清楚。
  3. 开源库的作者没有义务帮你解决问题,你不能强迫人家帮你。
  4. 这类问题的解决办法:如果你是Spring开发者,你可以用自带的AOP来解决;如果你是Android开发者,你可以用Android Gradle Plugin的Transform API来解决。
to94eoyn

to94eoyn12#

@leleliu008 问题1,2已经修改。
3:没有强迫
4:曲线救国,治标不治本
多谢建议

相关问题