用 Kotlin Data 类 替换 Java DTO 类 的问题

Issue with replacing Java DTO classes with Kotlin Data classes

我阅读了有关 Kotlin 数据 classes 的文章,并认为它们在描述数据传输对象 (DTO) 的情况下可能非常有用。在我的 Java 项目中,我已经在 Java 上编写了 DTO classes,例如:

public class Tweet {
    private String id;
    private String profileId;
    private String message;

    public Tweet() {}

    public String getId() {
        return id;
    }

    public String getProfileId() {
        return profileId;
    }

    public String getMessage() {
        return message;
    }

    public void setId(String id) {
        this.id = id;
    }

    public void setProfileId(String profileId) {
        this.profileId = profileId;
    }

    public Tweet setMessage(String message) {
        this.message = message;
        return this;
    }
}

这些 DTO classes 存储在单独的工件中,我将其添加为对其他工件的依赖项。所以,我决定用 Kotlin classes 替换它,并在 Kotlin 上重写了提到的 Tweet class,所以它开始看起来像:

data class Tweet(var id: String? = null,
                 var profileId: String? = null,
                 var message: String? = null)

这是我第一次使用 Kotlin,所以可能有些东西看起来很难看,但我的主要问题是 - 当我尝试重建使用我的 DTO 作为依赖项的工件时,我得到这样的异常:

at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2992) at io.vertx.core.json.Json.decodeValue(Json.java:117) at gk.tweetsched.api.repository.TweetRepository.get(TweetRepository.java:51) at gk.tweetsched.api.repository.TweetRepositoryTest.testGet(TweetRepositoryTest.java:68) Caused by: java.lang.ClassNotFoundException: kotlin.jvm.internal.DefaultConstructorMarker at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:335) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 67 more

据我所知,堆栈跟踪 Jackson 无法将 JSON 反序列化为 Tweet Kotlin class。

这是我的 Java 方法,我得到了那个异常:

public Tweet get(String id) {
    try (Jedis jedis = pool.getResource()) {
        return Json.decodeValue(jedis.hget(TWEETS_HASH, id), Tweet.class);
    } catch (Exception e) {
        LOGGER.error(e);
    }
    return null;
}

其中 Json class 来自 'io.vertx.core.json' 包。

我该如何解决这个问题?我应该在 Java 项目中进行哪些额外配置才能使用 Kotlin classes?

默认情况下,Jackson 需要一个无参数构造函数来将 JSON 反序列化为 class - Kotlin 数据 classes 没有,因此您需要添加一个 Jackson 模块来处理这个:

jackson-module-kotlin

编辑: 我已经阅读了 source for io.vertx.core.json.Json class,似乎 class 使用的两个对象映射器都存储在 public 静态字段中。

因此,要注册 jackson-module-kotlin,您需要将此代码段包含在您的应用程序初始化代码中(或其他任何地方,只要它在您尝试反序列化任何 Kotlin 数据 classes 之前执行) :

Json.mapper.registerModule(new KotlinModule())
Json.prettyMapper.registerModule(new KotlinModule()) 

就我而言,我在 java 中创建了 kotlin class DTO 实例以使用 RESTful Api。 现在我测试了 2 个解决方案:

  1. 在数据中使用无参数构造函数class。

reference kotlin 说:

On the JVM, if all of the parameters of the primary constructor have default values, the compiler will generate an additional parameterless constructor which will use the default values. This makes it easier to use Kotlin with libraries such as Jackson or JPA that create class instances through parameterless constructors.

所以我在 kotlin 中有这样一个 DTO:

    data class Dto (
        var id: Int?=null,
        var version: Int?=null, 
        var title: String?=null,
        var firstname: String?=null,
        var lastname: String?=null,
        var birthdate: String?=null

)

然后,我在 java 中创建 class 实例 DTO:

Dto dto = new Dto();
dto.setId(javadto.getId());
...
  1. 使用插件 jackson-module-kotlin

import com.fasterxml.jackson.annotation.JsonProperty

    data class Dto (
            @JsonProperty("id") var id: Int?,
            @JsonProperty("version") var version: Int?, 
            @JsonProperty("title") var title: String?,
            @JsonProperty("firstname") var firstname: String?,
            @JsonProperty("lastname") var lastname: String?,
            @JsonProperty("birthdate") var birthdate: String?,

    )

然后,我在 java 中创建 class 实例 DTO:

Dto dto = new Dto(null, null, null, null, null, null);
dto.setId(javadto.getId());
...