Ошибка с Gson (). From Json - «Не удалось вызвать publi c com.keikakupet.PetStatus () без аргументов» - PullRequest
1 голос
/ 13 апреля 2020

Я пытаюсь сохранить экземпляр моего Kotlin класса в файле JSON с помощью библиотеки Gson. Однако когда я запускаю Gson (). Из Json, я получаю следующую ошибку:

java .lang.RuntimeException: Не удалось вызвать publi c 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
        }
    }
}

Информация Logcat:

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

1 Ответ

0 голосов
/ 13 апреля 2020

Поскольку ваш конструктор не имеет параметров, 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() - это взломанный подход (i sh), я рекомендую вам на самом деле извлечь код и создать экземпляр класса извне.

...