无法从 Retrofit Call 获取数据到直接 JSON 文件

Unable to get data from Retrofit Call to a Direct JSON File

我正在使用 Jetpack Compose 开发一个小项目。 我正在尝试使用 Retrofit 来自此 url 的静态 JSON 文件中的数据。 https://firebasestorage.googleapis.com/v0/b/culoader.appspot.com/o/json%2Fcudata.json?alt=media&token=d0679703-2f6c-440f-af03-d4d61305cc84

网络模块

@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {

    @Provides
    @Singleton
    fun proveidesCurrencyService() : CurrencyService{
        return Retrofit.Builder()
            .baseUrl("https://firebasestorage.googleapis.com/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
            .create(CurrencyService::class.java)
    }
}

服务Class

interface CurrencyService {
    //@Streaming
    @GET
    suspend fun getCurrencyFile(@Url fileUrl:String): Response<ResponseBody>

}


data class CurrencyAPIResponse(

    @SerializedName("data") var data: MutableState<List<CurrencyRates>>

)

data class CurrencyRates (

    @SerializedName("code") var code : String,
    @SerializedName("value") var value : String

)

ViewModel


@HiltViewModel
class CurrencyDataViewModel
@Inject
constructor(private val currencyService: CurrencyService
) : ViewModel() 
{
 init {
        getCurrencyFileData()
    }
}

 fun getCurrencyFileData() = viewModelScope.launch(Dispatchers.IO) {
        val url: String =
            "https://firebasestorage.googleapis.com/v0/b/culoader.appspot.com/o/json%2Fcudata.json?alt=media&token=d0679703-2f6c-440f-af03-d4d61305cc84"

        val responseBody = currencyService.getCurrencyFile(url).body()
        if (responseBody != null) {
            Log.d("\nresponsebody", responseBody.string())
            val gson = GsonBuilder().setPrettyPrinting().create()
            val currencyAPIResponse = gson.fromJson(responseBody.string(), CurrencyAPIResponse::class.java)
            val data: MutableState<List<CurrencyRates>> = currencyAPIResponse.data
            Log.d("Data", data.value[0].code)

        }

    }

每次,我都会收到以下错误,

尝试在空对象引用上调用虚方法'androidx.compose.runtime.MutableState com.tuts.playlite.network.response.CurrencyAPIResponse.getData()'

不确定,在我失败的地方,我尝试将其转换为 JSON 对象,但仍然失败。获取数据的方式是否正确?

另一件事注意到,即使 JSON 文件在 url 中是完整的,响应正文日志显示 JSON 和一些其他内容。

{
          "code": "IMP",
          "value": "0.722603"
        },
        {
          "code": "INR",
    
    [          1631385414.170 12452:12478 D/
     responsebody]
          "value": "72.99465"
        },
        {
          "code": "IQD",
          "value": "1458.61356"
        },

因此,GSON 可能无法形成 json,因此可能会出现空异常。 不确定为什么会添加随机文本!

您已经提供了一个 Gson 转换器进行改造,因此改造应该已经能够为您完成 json 到对象的转换。这就是改造之美!

尝试像这样重写您的 CurrencyService:

interface CurrencyService {
    
    @GET("v0/b/culoader.appspot.com/o/json%2Fcudata.json?alt=media&token=d0679703-2f6c-440f-af03-d4d61305cc84")
    suspend fun getCurrencyFile(): CurrencyAPIResponse
}

您的 ViewModel 也有一些问题。不确定您是否真正指的是 MutableState,但我猜您正在寻找 MutableLiveData 或 MutableStateFlow。下面是一个使用 MutableLiveData 的例子。

@HiltViewModel
class CurrencyDataViewModel @Injectconstructor(
private val currencyService: CurrencyService
) : ViewModel() {

  private val _currencyData = MutableLiveData<List<CurrencyRates>>()
  private val currencyData: LiveData = _currencyData

  init {
     getCurrencyFileData()
  }

  fun getCurrencyFileData() = viewModelScope.launch(Dispatchers.IO) {
     _currencyData.postValue(currencyService.getCurrencyFile().data)
  }
}

使用 Kotin Coroutines 进行改造,试试下面的方法

interface CurrencyService {
 @GET("v0/b/culoader.appspot.com/o/json%2Fcudata.json?alt=media&token=d0679703-2f6c-440f-af03-d4d61305cc84")
  suspend fun getCurrencyFile(): Response<CurrencyAPIResponse>
}

如果您使用的是 MVVM,请使用此存储库 class

suspend fun getCurrencyFile:Response<CurrencyAPIResponse>{
    return currencyService.getCurrencyFile()
}

然后在您的视图模型中 class

    Coroutines.main {
            try{
               val response = repository.getCurrencyFile()
               if(response.isSuccessful){
                   //response.body is your data
                }
            }catch(Exception){}
     }

如果你没有使用存储库class你可以跳过第二步直接在viewmodel中调用服务class, 协程代码是

object Coroutines {
    fun main(work:suspend (()->Unit)) =
        CoroutineScope(Dispatchers.Main).launch {
            work()
        }
}

最后,罪魁祸首似乎是responseBody Stream。在我用下面更改代码后,它似乎可以正常工作。我假设,它无法更早地获得正确的完整 JSON 并抛出空对象引用错误。

    val responseBody = currencyService.getCurrencyFile(url).body()
    val resData = responseBody?.byteStream()!!.bufferedReader().use {
        it.readText()
    }
    val gson = GsonBuilder().setPrettyPrinting().create()
    val currencyAPIResponse = gson.fromJson(resData, CurrencyAPIResponse::class.java)

感谢大家的支持!