Skip to content
New issue

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

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

Already on GitHub? # to your account

[BUG]Kotlin 反序列化错误 #276

Closed
HowardLei opened this issue May 18, 2022 · 11 comments
Closed

[BUG]Kotlin 反序列化错误 #276

HowardLei opened this issue May 18, 2022 · 11 comments
Assignees
Labels
bug Something isn't working
Milestone

Comments

@HowardLei
Copy link
Contributor

HowardLei commented May 18, 2022

问题描述

Kotlin 反序列化错误

环境信息

请填写以下信息:

  • OS信息: [e.g.:macOS 12.3.1]
  • JDK信息: [e.g.:Oracle JDK 11.0.11]
  • Kotlin信息: [e.g.:1.6.20]
  • 版本信息:[e.g.:fastjson2-kotlin 2.0.3]

重现步骤

如何操作可以重现该问题:

    @Test
    fun testKotlin() {
        val json = """
        {"access_token":"MTUZNGNKNMITZTVMMC0ZYTY0LWFIZJCTMZJLMDIYMMY4OGUW","scope":""}
    """.trimIndent()
        val accessTokenResponse = JSON.parseObject(json, AccessTokenResponse::class.java)
        log.info(accessTokenResponse.toString())
    }

data class AccessTokenResponse(
    @JSONField(name = "access_token")
    var accessToken: String? = null,
    @JSONField(name = "expires_in")
    var expiresIn: Int? = null,
    @JSONField(name = "scope")
    var scope: String? = null,
    @JSONField(name = "token_type")
    var tokenType: String? = null
): Serializable

期待的正确结果

AccessTokenResponse(accessToken=MTUZNGNKNMITZTVMMC0ZYTY0LWFIZJCTMZJLMDIYMMY4OGUW, expiresIn=null, scope=, tokenType=null)

相关日志输出

请复制并粘贴任何相关的日志输出。

附加信息

截屏2022-05-18 12 09 31

@HowardLei HowardLei added the bug Something isn't working label May 18, 2022
@kraity
Copy link
Collaborator

kraity commented May 18, 2022

你好, 这里JSONField注解作用在构造函数的值参数时, 写法有要求, 需要在JSONField前面加field:

data class AccessTokenResponse(
    @field:JSONField(name = "access_token")
    var accessToken: String? = null,
    @field:JSONField(name = "expires_in")
    var expiresIn: Int? = null,
    @field:JSONField(name = "scope")
    var scope: String? = null,
    @field:JSONField(name = "token_type")
    var tokenType: String? = null
): Serializable

@kraity kraity mentioned this issue May 18, 2022
3 tasks
@HowardLei
Copy link
Contributor Author

HowardLei commented May 18, 2022

你好, 这里JSONField注解作用在构造函数的值参数时, 写法有要求, 需要在JSONField前面加field:

data class AccessTokenResponse(
    @field:JSONField(name = "access_token")
    var accessToken: String? = null,
    @field:JSONField(name = "expires_in")
    var expiresIn: Int? = null,
    @field:JSONField(name = "scope")
    var scope: String? = null,
    @field:JSONField(name = "token_type")
    var tokenType: String? = null
): Serializable

想请问一下,这个地方加 @field 原因是什么?有什么特殊意义吗?

@kraity
Copy link
Collaborator

kraity commented May 18, 2022

因为需要注解的字段是在构造函数里
它会被kotlin编译为构造函数的参数、setter、getter 这里上下文比较多
使用field:明确的指定作用于属性的backing field

wenshao added a commit that referenced this issue May 18, 2022
@kraity
Copy link
Collaborator

kraity commented May 18, 2022

想请问一下,这个地方加 @field 原因是什么?有什么特殊意义吗?

在kotlin还有@field @set @get @file @param等等, 注解目标位置的方式

@HowardLei
Copy link
Contributor Author

想请问一下,这个地方加 @field 原因是什么?有什么特殊意义吗?

在kotlin还有@field @set @get @file @param等等, 注解目标位置的方式

那这个地方为什么只在反序列化中需要增加,而序列化中不进行考虑?

@kraity
Copy link
Collaborator

kraity commented May 18, 2022

那这个地方为什么只在反序列化中需要增加,而序列化中不进行考虑?

你可以去掉@field测试, 你会发现序列结果类似{"accessToken":"123","expiresIn":1,"scope":"2","tokenType":"3"} 与预期key不合的

这里注解没有生效, 默认使用getter的方法名作为key, kotlin会自动将字段编译它的getter setter
var accessToken 会编译它的setAccessToken getAccessToken, 所以序列化时候取accessToken作为key

你可以使用 @field:JSONField(...) @set:JSONField(...) @get:JSONField(...) 测试序列和反序列化, 对比一下结果

使用@field在序列和反序列化都有作用

@kraity
Copy link
Collaborator

kraity commented May 18, 2022

你好, 我再汇总一下吧,

data class AccessTokenResponse(
    @JSONField(name = "access_token")
    var accessToken: String? = null
)

上面的accessToken会编译有accessToken field, setAccessToken, getAccessToken,
JSONField注解的位置则是在构造参数里, 反编译后的java表示大概是是这样

class AccessTokenResponse{
     private  String accessToken;

     public AccessTokenResponse(@JSONField(...) String accessToken){
         this.accessToken=accessToken;
     }

     public String getAccessToken(){........}
     public void setAccessToken(.....){........}
}

反编译发现JSONField注解在了构造参数上, 而fastjson2是通过Fastjson2目前是对setter、getter以及field关联的
所以这样是不符合你的预期的

@field:JSONField指定注解位置后

data class AccessTokenResponse(
    @field:JSONField(name = "access_token")
    var accessToken: String? = null
)

反编译大概这样

class AccessTokenResponse{
     @JSONField(....)
     private  String accessToken;

     public AccessTokenResponse(String accessToken){
         this.accessToken=accessToken;
     }

     public String getAccessToken(){........}
     public void setAccessToken(.....){........}
}

这样在目前的版本就注解生效符合你的预期, 后期fastjson2会对kotlin这种在构造函数里注解做支持的, 敬请期待!

@set:JSONField指定注解位置后

class AccessTokenResponse{
     private  String accessToken;

     public AccessTokenResponse(String accessToken){
         this.accessToken=accessToken;
     }

     public String getAccessToken(){........}

     @JSONField(....)
     public void setAccessToken(.....){........}
}

这样你应该知道了为何这样写吧

wenshao added a commit that referenced this issue May 18, 2022
@wenshao wenshao added this to the 2.0.4 milestone May 18, 2022
@wenshao
Copy link
Member

wenshao commented May 18, 2022

https://oss.sonatype.org/content/repositories/snapshots/com/alibaba/fastjson2/fastjson2-kotlin/2.0.4-SNAPSHOT/

已经支持,你可以用2.0.4-SNAPSHOT版本验证,需要依赖

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>

@HowardLei
Copy link
Contributor Author

你好, 我再汇总一下吧,

data class AccessTokenResponse(
    @JSONField(name = "access_token")
    var accessToken: String? = null
)

上面的accessToken会编译有accessToken field, setAccessToken, getAccessToken, 而JSONField注解的位置则是在构造参数里, 反编译后的java表示大概是是这样

class AccessTokenResponse{
     private  String accessToken;

     public AccessTokenResponse(@JSONField(...) String accessToken){
         this.accessToken=accessToken;
     }

     public String getAccessToken(){........}
     public void setAccessToken(.....){........}
}

反编译发现JSONField注解在了构造参数上, 而fastjson2是通过Fastjson2目前是对setter、getter以及field关联的 所以这样是不符合你的预期的

@field:JSONField指定注解位置后

data class AccessTokenResponse(
    @field:JSONField(name = "access_token")
    var accessToken: String? = null
)

反编译大概这样

class AccessTokenResponse{
     @JSONField(....)
     private  String accessToken;

     public AccessTokenResponse(String accessToken){
         this.accessToken=accessToken;
     }

     public String getAccessToken(){........}
     public void setAccessToken(.....){........}
}

这样在目前的版本就注解生效符合你的预期, 后期fastjson2会对kotlin这种在构造函数里注解做支持的, 敬请期待!

@set:JSONField指定注解位置后

class AccessTokenResponse{
     private  String accessToken;

     public AccessTokenResponse(String accessToken){
         this.accessToken=accessToken;
     }

     public String getAccessToken(){........}

     @JSONField(....)
     public void setAccessToken(.....){........}
}

这样你应该知道了为何这样写吧

感谢大佬的指点,我通过这次交流也收益匪浅,之前从来没有注意在构造函数中也有可能生成注解。

@HowardLei
Copy link
Contributor Author

HowardLei commented May 18, 2022

https://oss.sonatype.org/content/repositories/snapshots/com/alibaba/fastjson2/fastjson2-kotlin/2.0.4-SNAPSHOT/

已经支持,你可以用2.0.4-SNAPSHOT版本验证,需要依赖

        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
        </dependency>

已验证,感谢温少的支持

@HowardLei HowardLei reopened this May 18, 2022
@wenshao wenshao closed this as completed May 18, 2022
@wenshao
Copy link
Member

wenshao commented May 21, 2022

https://github.com/alibaba/fastjson2/releases/tag/2.0.4
问题已经在2.0.4版本中修复,请用新版本

# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants