【问题标题】:Deserialize a Kotlin Data Class with Google HTTP Client使用 Google HTTP 客户端反序列化 Kotlin 数据类
【发布时间】:2018-03-28 16:26:00
【问题描述】:

我正在使用 Google HTTP 客户端发出 API 请求,我希望使用 parseAs() 方法为我反序列化 JSON 响应。我的模型是 Kotlin 数据类,但反序列化器无法实例化我的类:

Caused by: java.lang.IllegalArgumentException: unable to create new instance of class com.ippontech.blog.stats.model.GitFile because it has no accessible default constructor

这是我的 Kotlin 数据类:

data class GitFile(
        val name: String,
        @SerializedName("download_url") val downloadUrl: String)

以及我使用的代码:

val requestFactory = NetHttpTransport().createRequestFactory{
    it.setParser(JsonObjectParser(GsonFactory()))
}

val request = requestFactory.buildGetRequest(
        GenericUrl("https://api.github.com/repos/ippontech/blog-usa/contents/posts"))
val type = object : TypeToken<List<GitFile>>() {}.type
val rawResponse = request.execute().parseAs(type)

另一方面,如果我将响应作为字符串检索,然后使用 Gson 解析器,它可以正常工作:

val requestFactory = NetHttpTransport().createRequestFactory()
val request = requestFactory.buildGetRequest(
        GenericUrl("https://api.github.com/repos/ippontech/blog-usa/contents/posts"))
val rawResponse = request.execute().parseAsString()
val type = object : TypeToken<List<GitFile>>() {}.type
val gitFiles: List<GitFile> = Gson().fromJson(rawResponse, type)

这似乎表明JsonObjectParser 在这里没有完成它的工作。

我正在使用的版本:

compile 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.30'
compile 'com.google.code.gson:gson:2.8.0'
compile 'com.google.apis:google-api-services-sheets:v4-rev512-1.23.0'
compile 'com.google.api-client:google-api-client-gson:1.23.0'

知道如何解决这个问题吗?

【问题讨论】:

    标签: kotlin gson


    【解决方案1】:

    这是因为底层的JsonObjectParser 方法使用newInstance in:

    public static <T> T newInstance(Class<T> clazz) {
        try {
            return clazz.newInstance();
        } catch (IllegalAccessException var2) {
            throw handleExceptionForNewInstance(var2, clazz);
        } catch (InstantiationException var3) {
            throw handleExceptionForNewInstance(var3, clazz);
        }
    }
    

    虽然您的 GitFile 具有唯一不是默认构造函数(无参数)的构造函数,但它清楚地告诉了您。

    从某种角度来看,您可以像这样添加默认值:

    data class GitFile(
            val name: String,
            @SerializedName("download_url") val downloadUrl: String
    ) {
        constructor() : this(name = "", downloadUrl = "")
    }
    

    这不会失败,但似乎工作错误:解析的对象属性对我来说都是 null(我不知道在配置 JsonObjectParser 实例时我缺少什么)。

    Gson 不需要默认构造函数并且可以无缝工作。 但它似乎需要一个自定义的ObjectParser 实现,我相信它会运行得更快:

    class GsonObjectParser(private val gson: Gson) : ObjectParser {
    
        override fun <T : Any?> parseAndClose(inputStream: InputStream?, charset: Charset?, clazz: Class<T>?): T {
            return doParseAndClose(InputStreamReader(inputStream, charset), clazz);
        }
    
        override fun parseAndClose(inputStream: InputStream?, charset: Charset?, type: Type?): Any {
            return doParseAndClose(InputStreamReader(inputStream, charset), type);
        }
    
        override fun <T : Any?> parseAndClose(reader: Reader?, clazz: Class<T>?): T {
            return doParseAndClose(reader, clazz);
        }
    
        override fun parseAndClose(reader: Reader?, type: Type?): Any {
            return doParseAndClose(reader, type);
        }
    
        private fun <T : Any?> doParseAndClose(reader: Reader?, type: Type?): T {
            JsonReader(reader).use {
                return gson.fromJson(reader, type);
            };
        }
    
    }
    
    data class GitFile(
        val name: String?,
        val downloadUrl: String?
    )
    
    fun main(args: Array<String>) {
    
        val gson = GsonBuilder()
                .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
                .create();
        val requestFactory = NetHttpTransport().createRequestFactory {
            it.setParser(GsonObjectParser(gson))
        }
        val request = requestFactory.buildGetRequest(GenericUrl("https://api.github.com/repos/ippontech/blog-usa/contents/posts"))
        val type = object : TypeToken<List<GitFile>>() {}.type
        val gitFiles = request.execute().parseAs(type) as List<GitFile>
        gitFiles
                .stream()
                .map { gitFile -> gitFile.name + ": " + gitFile.downloadUrl }
                .forEach { gitFile -> System.out.println(gitFile) }
    }
    

    截至今天的一些第一行输出:

    10-tips-and-tricks-for-cassandra.md: https://raw.githubusercontent.com/ippontech/blog-usa/master/posts/10-tips-and-tricks-for-cassandra.md
    5-laws-every-developer-should-know.md: https://raw.githubusercontent.com/ippontech/blog-usa/master/posts/5-laws-every-developer-should-know.md
    a-journey-into-blockchain-private-network-with-ethereum.md: https://raw.githubusercontent.com/ippontech/blog-usa/master/posts/a-journey-into-blockchain-private-network-with-ethereum.md
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-09-29
      • 1970-01-01
      相关资源
      最近更新 更多