首页 > 解决方案 > Gson().fromJson 出错 - “无法在没有参数的情况下调用公共 com.keikakupet.PetStatus()”

问题描述

我正在尝试使用 Gson 库将我的 Kotlin 类的实例存储到 JSON 文件中。但是,当我运行 Gson().fromJson 时,我收到以下错误:

java.lang.RuntimeException:无法调用没有参数的公共 com.keikakupet.PetStatus()

我对错误的理解是 Gson 要求我的类有一个不带参数的主构造函数,因此它可以构造所需的对象(在本例中为 PetStatus 对象)。但是,我有这样一个构造函数。我不确定部分问题是否在于我正在从 init 运行该方法。有谁知道我可以如何解决这个错误?

我的代码:

package com.keikakupet

import android.content.Context
import android.util.Log
import com.google.gson.Gson
import java.io.File
import java.util.*
import java.io.BufferedReader

class PetStatus constructor(){

    var maxHealth: Int = 10
    var currentHealth: Int = 10
    var healthIncrementer: Int = 2 // amount by which health increments when a task is completed
    var healthDecrementer: Int = 0 // amount by which health decrements when user approaches / misses deadline
    var isHungry: Boolean = false
    var isTired: Boolean = false
    var isSick: Boolean = false
    var isAlive: Boolean = true

    init{
        //if a json exists, use it to update PetStatus
        val context = getContext()
        var file = File(context.getFilesDir(), "PetStatus.json")
        if(file.exists()){
            Log.d("FILE_EXISTS", "File exists!")

            val bufferedReader: BufferedReader = file.bufferedReader()
            val json = bufferedReader.readText()

            val retrievedStatus = Gson().fromJson(json, PetStatus::class.java)

            Log.d("JSON_RETRIEVED", json)
        }
        else
            Log.d("FILE_DNE", "File does not exist!")
            updateJson()
    }

    // method to update pet's health and ailment upon completing a task
    fun processCompletedTask(){
        incrementHealth()
        removeAilment()
        Log.d("TASK_COMPLETE", "completed task processed")
    }

    fun getHealthPercent(): Int {
        return currentHealth / maxHealth
    }

    // method to update pet's health and ailment upon missing a task
    fun processMissedTask(){
        decrementHealth()
        addAilment()
        Log.d("TASK_MISSED", "missed task processed")
    }

    /*
    Potentially creating another method to update pet's
    health and status because of an approaching deadline.
     */

    // method to decrement the pet's health
    private fun decrementHealth(){
        currentHealth-=healthDecrementer
        if(currentHealth <= 0)
            isAlive = false
        updateJson()
    }

    // method to increment the pet's health
    private fun incrementHealth(){
        val sum = currentHealth + healthIncrementer
        if(sum > maxHealth)
            currentHealth = maxHealth
        else
            currentHealth = sum
        updateJson()
    }

    // method to add an ailment to the pet
    private fun addAilment(){
        // if no ailment, randomly assign hungry or tired
        if(!isHungry && !isTired && !isSick){
            val rand = Random()
            val randBool = rand.nextBoolean()
            if(randBool)
                isHungry = true
            else
                isTired = true
            healthDecrementer = 1
        }

        // otherwise, if hungry XOR tired, assign the other
        else if(isHungry && !isTired){
            isTired = true
            healthDecrementer = 2
        }
        else if(isTired && !isHungry){
            isHungry = true
            healthDecrementer = 2
        }

        // otherwise, if both hungry AND tired, assign sick
        else if(isHungry && isTired){
            isSick = true
            healthDecrementer = 3
        }

        updateJson()
    }

    // method to remove an ailment from the pet
    private fun removeAilment(){
        // if sick, remove sick
        if(isSick){
            isSick = false
            healthDecrementer = 2
        }

        // otherwise, if hungry and tired, remove one of the two randomly
        else if(isHungry && isTired){
            val rand = Random()
            val randBool = rand.nextBoolean()
            if(randBool)
                isHungry = false
            else
                isTired = false
            healthDecrementer = 1
        }

        // otherwise, if hungry XOR tired, remove relevant ailment
        else if(isHungry && !isTired){
            isHungry = false
            healthDecrementer = 0
        }
        else if(isTired){
            isTired = false
            healthDecrementer = 0
        }

        updateJson()
    }

    private fun updateJson(){
        val gson = Gson()
        var json: String = gson.toJson(this)
        Log.d("JSON_UPDATE", json)

        val context = getContext()
        var file = File(context.getFilesDir(), "PetStatus.json")
        file.writeText(json)

        val bufferedReader: BufferedReader = file.bufferedReader()
        json = bufferedReader.readText()
        Log.d("JSON_FROM_FILE", json)
    }

    companion object {

        private lateinit var context: Context

        fun setContext(con: Context) {
            context=con
        }

        fun getContext() : Context {
            return context
        }
    }
}

日志信息:

Caused by: java.lang.RuntimeException: Failed to invoke public com.keikakupet.PetStatus() with no args
        at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:118)
        at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:212)
        at com.google.gson.Gson.fromJson(Gson.java:927)
        at com.google.gson.Gson.fromJson(Gson.java:892)
        at com.google.gson.Gson.fromJson(Gson.java:841)
        at com.google.gson.Gson.fromJson(Gson.java:813)
        at com.keikakupet.PetStatus.<init>(PetStatus.kt:32)
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at com.google.gson.internal.ConstructorConstructor$3.construct(ConstructorConstructor.java:110)
        	... 4981 more

标签: android-studiokotlingson

解决方案


由于您的构造函数没有参数,因此 Gson 无法在 json 的帮助下实例化您的类。

像这样重组你的类:

class PetStatus private constructor(
    var maxHealth: Int = 10,
    var currentHealth: Int = 10,
    var healthIncrementer: Int = 2, // amount by which health increments when a task is completed
    var healthDecrementer: Int = 0, // amount by which health decrements when user approaches / misses deadline
    var isHungry: Boolean = false,
    var isTired: Boolean = false,
    var isSick: Boolean = false,
    var isAlive: Boolean = true) {

    /**
     * This is optional fix, since it is a design guideline
     * recommendation, you can retain your original function as well
     * fun getHealthPercent(): Int {
     *     return currentHealth / maxHealth
     * }
     */
    val healthPercent: Int
        get() = currentHealth / maxHealth

    ...

    companion object {

        lateinit var context: Context // getters and setters for java are automatically generated

        operator fun invoke(): PetStatus {
            //if a json exists, use it to update PetStatus
            val context = context
            var file = File(context.getFilesDir(), "PetStatus.json")
            if(file.exists()){
                Log.d("FILE_EXISTS", "File exists!")

                val bufferedReader: BufferedReader = file.bufferedReader()
                val json = bufferedReader.readText()

                val retrievedStatus = Gson().fromJson(json, PetStatus::class.java)

                Log.d("JSON_RETRIEVED", json)

                return retrievedStatus
            } else {
                Log.d("FILE_DNE", "File does not exist!")
                return PetStatus()
            }
            updateJson()
        }

        operator fun invoke(maxHealth: Int, currentHealth: Int, healthIncrementer: Int, healthDecrementer: Int, isHungry: Boolean, isTired: Boolean, isSick: Boolean, isAlive: Boolean): PetStatus
            = PetStatus(maxHealth, currentHealth, healthIncrementer, healthDecrementer, isHungry, isTired, isSick, isAlive)
    }
}

现在你可以调用这个类

PetStatus()

operator fun invoke()是一种 hack (ish) 方法,我建议您实际上应该取出代码并从外部实例化该类。


推荐阅读