Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] 序列化及反序列化 kotlin 的 value class 的逻辑存在严重漏洞 #3288

Open
EmptyDreams opened this issue Jan 16, 2025 · 2 comments
Labels
bug Something isn't working

Comments

@EmptyDreams
Copy link
Contributor

EmptyDreams commented Jan 16, 2025

问题描述

当一个类中包含了一个 value class 时,fastjson2 序列化为 json 的时候会给这个字段添加一个固定的后缀,即使我通过 JSONField 手动指定名称,仍然会携带该后缀,这样子序列化出来的 json 不方便其它地方解析。

在 fastjson2 解序列化时,却不会添加这个后缀,这导致 fastjson2 无法解序列化自己序列化的 json,这非常的不合理。

更严重的问题是,当 value class 内部是一个基础类型时,fastjson2 永远将 value class 当做基础类型处理,所以在反序列化遇到装箱时就会出现类似的错误:

Caused by: java.lang.IllegalArgumentException: argument type mismatch
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:65)
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
	... 32 common frames omitted
Caused by: java.lang.ClassCastException: Cannot cast java.lang.Integer to kotlin.jvm.internal.DefaultConstructorMarker
	at java.base/java.lang.Class.cast(Class.java:4067)
	at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
	... 35 common frames omitted

环境信息

  • OS信息: macOS 15.1.1 (24B91) 2.6 GHz 六核Intel Core i7
  • JDK信息: graalvm 21.0.5
  • 版本信息:2.0.53

重现步骤

编写如下代码:

@JvmInline
value class Test(val a: Int)

data class Test2(
//    @JSONField(name = "b")
    val b: Test
)

fun main() {
    println(Test(1).toJSONString())
    println(JSON.parseObject("{\"a\":1}", Test::class.java))
    println(Test2(Test(1)).toJSONString())
    println(JSON.parseObject("{\"b-C9M8Yyk\":2}", Test2::class.java))
    println(JSON.parseObject("{\"b\":2}", Test2::class.java))
}

最终得到如下输出:

{"a":1}
Test(a=1)
{"b-C9M8Yyk":1}        // 取消掉 JSONField 的注释后结果仍为这样
Test2(b=Test(a=0))    // 这两行的结果应该和序列化的行为保持一致,而不是两边使用两个命名规则
Test2(b=Test(a=2))
@EmptyDreams EmptyDreams added the bug Something isn't working label Jan 16, 2025
@EmptyDreams
Copy link
Contributor Author

EmptyDreams commented Jan 16, 2025

我仔细检查了一下代码、字节码和文档,发现 koltin 编译器会把属性全都编译为 private,然后为其生成 getter/setter,所以在 fastjson2 序列化的时跳过了这一 field,但是由于 kotlingetter 设置为 public,所以 fastjson2 通过 getter 将值序列化到了 json 中。

问题在于,kotlin 为了避免混淆,会给 getter(及其它用 value class 做参数的函数,参见:kotlin 文档)添加一个哈希值,结果 getter 的命名就变成了 getB-<hash> 的格式,于是 fastjson2 就把这个作为变量名进行提取了。

所以通过下面两种方法可以解决这个问题:

data class Test2(
    @get:JvmName("getB")
    val b: Test
)
data class Test2(
    @get:JSONField(name = "b")
    val b: Test
)

@EmptyDreams
Copy link
Contributor Author

同理,对于 internal 修饰的属性,kotlin 会给 getter 添加 $<moduleName> 后缀,所以直接写 @JSONField 也是没有任何效果的,需要写 @get:JSONField

我认为这虽然不是什么致命的问题,但是最好能想办法处理一下。

@EmptyDreams EmptyDreams changed the title [BUG] 序列化 kotlin 的 value class 时 key 会添加一个后缀且无法消除 [BUG] 序列化及反序列化 kotlin 的 value class 的逻辑存在严重漏洞 Jan 17, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant