jackson是个常用的java json库,功能很强大。
kotlin中有一个很好的特性叫data class
和lombok
的@Data
很类似,会自动帮类生成getter/setter/hashCode/equal/toString。
语法也很简洁
data class Foo(val bar:String, val fuck:Int)
但是当我们要同时使用data class和jackson的时候问题就来了。
直接对上面Foo类进行反序列化时
ObjectMapper().readValue("""{"bar":"a","fuck":1}""", Foo::class.java)
会出现类似异常
Exception in thread "main" com.fasterxml.jackson.databind.JsonMappingException: Can not construct instance of test.Foo: no suitable constructor found, can not deserialize from Object value (missing default constructor or creator, or perhaps need to add/enable type information?)
at [Source: {"bar":"a","fuck":1}; line: 1, column: 2]
at com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1469)
at com.fasterxml.jackson.databind.DeserializationContext.handleMissingInstantiator(DeserializationContext.java:1012)
...
其实看原因很简单,没有默认构造方法或JsonCreator
的方法。
当我们想对字段自定义json key的时候,通常会使用@JsonProperty
来指定field name。就像下面这样
data class Foo(val bar:String, @JsonProperty("a") val fuck:Int)
但是会发现注解好像一点作用都没有
而当我们相对某些字段采用特殊序列化方法的时候,会用@JsonSerialize/@JsonDeserialize
data class Foo(val bar:String, @JsonSerialize(using = XXX::class) val fuck:Int)
但是也会发现注解好像一点作用都没有
这时候我们来面向google编程,通常会有以下几种方案
其实用jackson官方的kotlin模块是最简单有效的方法,但是对于我们目前遇到的场景有一个非常致命的问题,那就是我们会使用proguard来混淆kotlin代码。一般来说是没有问题的,但是jackson kotlin module会从kotlin编译器给每个类添加的@Metadata
注解获取反射信息,而这个注解内的内容proguard混淆时不会处理。最终会导致混淆后的代码经常出现各种ClassNotFoundException。所以我们目前的场景不能使用这种方案。
最后还是只能使用基于jvm的基础方法。
虽然kotlin号称对java 100%兼容,但是语法上由于多了很多东西,所以实际兼容的时候还会有很多技巧在其中,下面的样例代码其实就很好的解释了为什么平时在Java中的注解对kotlin一点用处都没有
@AnnotationClass
data class Foo @AnnotationConstructorMethod constructor(
@AnnotationConstructorParameter
val field : String
) {
@AnnotationMethod fun method1(@AnnotationParam param:Int) {}
}
像是@JsonCreator这种需要在构造函数等方法上使用的注解,需要在类名后增加constructor
关键字来使用。
而对类构造参数直接添加的注解实际上是被当成构造函数参数的注解对待的,所以这时候并不起真正的作用。而我们可以通过kotlin的use-site target
特性来给具体字段的不同场景增加注解。
class Example(@field:Ann val foo, // annotate Java field
@get:Ann val bar, // annotate Java getter
@param:Ann val quux) // annotate Java constructor parameter
就像这样在普通的注解前根据需要增加限定范围。所以当了解到这里的之后我们就知道,并不是jackson和kotlin不兼容,只是我们使用的姿势不对。
当我们能正确使用kotlin的注解之后,不管是jackson还是别的库,很多问题也就迎刃而解了。
具体详细注解文档可以参见官方文档
最后给个例子
Java类如下
@AnnotationClass
public class Demo {
@AnnotationField
private String field;
@AnnotationGetter
public String getField() {
return field;
}
@AnnotationSetter
public void setField(@AnnotationConstructorParam String field) {
this.field = field;
}
@AnnotationConstructor
public Demo(@AnnotationConstructorParam String field) {
this.field = field;
}
@AnnotationConstructor
public Demo(@AnnotationConstructorParam String field, @AnnotationConstructorParam String field2) {
this.field = field;
this.field2 = field2;
}
@AnnotationField
private String field2 = "1";
@AnnotationGetter
public String getField2() {
return field2;
}
@AnnotationSetter
public void setField2(@AnnotationSetterParam String field2) {
this.field2 = field2;
}
}
可以转化成如下kotlin类,当然并不是只有这一种方式。
@AnnotationClass
data class Demo @AnnotationConstructor constructor(
@field:AnnotationField
@get:AnnotationGetter
@set:AnnotationSetter
@AnnotationConstructorParam
var field: String? = null
) {
@AnnotationField
@get:AnnotationGetter
@set:AnnotationSetter
var field2: String = "1"
@AnnotationConstructor
constructor(@AnnotationConstructorParam field: String, @AnnotationConstructorParam field2: String) : this(field) {
this.field2 = field2
}
}
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
扫码关注腾讯云开发者
领取腾讯云代金券
Copyright © 2013 - 2025 Tencent Cloud. All Rights Reserved. 腾讯云 版权所有
深圳市腾讯计算机系统有限公司 ICP备案/许可证号:粤B2-20090059 深公网安备号 44030502008569
腾讯云计算(北京)有限责任公司 京ICP证150476号 | 京ICP备11018762号 | 京公网安备号11010802020287
Copyright © 2013 - 2025 Tencent Cloud.
All Rights Reserved. 腾讯云 版权所有