PID:18319 java.lang.IollegalStateException:应为BEGIN_ARRAY,但在第



我尝试使用Jetpack Compose和Kotlin 在Android中使用Spoonacular API

但是我收到一个错误

开始_ARRAY,但在第1行第2列路径$处开始_OBJECT

我不知道如何解决这个问题

这是我的JSON格式

{
"results": [
{
"vegetarian": true,
"vegan": true,
"glutenFree": true,
"dairyFree": true,
"veryHealthy": false,
"cheap": false,
"veryPopular": false,
"sustainable": false,
"lowFodmap": false,
"weightWatcherSmartPoints": 0,
"gaps": "GAPS_FULL",
"preparationMinutes": -1,
"cookingMinutes": -1,
"aggregateLikes": 1,
"healthScore": 51,
"creditsText": "Foodista.com – The Cooking Encyclopedia Everyone Can Edit",
"license": "CC BY 3.0",
"sourceName": "Foodista",
"pricePerServing": 81.7,
"extendedIngredients": [
{
"id": 11080,
"aisle": "Produce",
"image": "beets.jpg",
"consistency": "SOLID",
"name": "beet",
"nameClean": "red beet",
"original": "1 Beet, peeled",
"originalName": "Beet, peeled",
"amount": 1.0,
"unit": "",
"meta": [
"peeled"
],
"measures": {
"us": {
"amount": 1.0,
"unitShort": "",
"unitLong": ""
},
"metric": {
"amount": 1.0,
"unitShort": "",
"unitLong": ""
}
}
},
{
"id": 11124,
"aisle": "Produce",
"image": "sliced-carrot.png",
"consistency": "SOLID",
"name": "carrots",
"nameClean": "carrot",
"original": "1 pound Carrots, peeled",
"originalName": "Carrots, peeled",
"amount": 1.0,
"unit": "pound",
"meta": [
"peeled"
],
"measures": {
"us": {
"amount": 1.0,
"unitShort": "lb",
"unitLong": "pound"
},
"metric": {
"amount": 453.592,
"unitShort": "g",
"unitLong": "grams"
}
}
},
{
"id": 9200,
"aisle": "Produce",
"image": "orange.png",
"consistency": "SOLID",
"name": "oranges",
"nameClean": "orange",
"original": "2 pounds Oranges, peeled",
"originalName": "Oranges, peeled",
"amount": 2.0,
"unit": "pounds",
"meta": [
"peeled"
],
"measures": {
"us": {
"amount": 2.0,
"unitShort": "lb",
"unitLong": "pounds"
},
"metric": {
"amount": 907.185,
"unitShort": "g",
"unitLong": "grams"
}
}
}
],
"id": 1095729,
"title": "Immunity Booster Beet, Carrot & Orange Juice",
"readyInMinutes": 10,
"servings": 4,
"sourceUrl": "https://www.foodista.com/recipe/HHM34WCD/immune-booster-beet-carrot-orange-juice",
"image": "https://spoonacular.com/recipeImages/1095729-312x231.jpg",
"imageType": "jpg",
"summary": "Need a <b>gluten free, dairy free, paleolithic, and lacto ovo vegetarian beverage</b>? Immunity Booster Beet, Carrot & Orange Juice could be an outstanding recipe to try. This recipe makes 4 servings with <b>162 calories</b>, <b>4g of protein</b>, and <b>1g of fat</b> each. For <b>82 cents per serving</b>, this recipe <b>covers 18%</b> of your daily requirements of vitamins and minerals. This recipe from Foodista has 1 fans. If you have beet, carrots, oranges, and a few other ingredients on hand, you can make it. From preparation to the plate, this recipe takes roughly <b>10 minutes</b>. Overall, this recipe earns an <b>amazing spoonacular score of 89%</b>. If you like this recipe, you might also like recipes such as <a href="https://spoonacular.com/recipes/goji-orange-turmeric-smoothie-the-immunity-booster-610828">Goji-Orange-Turmeric Smoothie (“The Immunity Booster”)</a>, <a href="https://spoonacular.com/recipes/rejuvenating-root-juice-carrot-beet-blood-orange-ginger-turmeric-juice-1226953">Rejuvenating Root Juice | Carrot, Beet, Blood Orange, Ginger, Turmeric Juice</a>, and <a href="https://spoonacular.com/recipes/rejuvenating-root-juice-carrot-beet-blood-orange-ginger-turmeric-juice-1238761">Rejuvenating Root Juice | Carrot, Beet, Blood Orange, Ginger, Turmeric Juice</a>.",
"cuisines": [

],
"dishTypes": [
"beverage",
"drink"
],
"diets": [
"gluten free",
"dairy free",
"paleolithic",
"lacto ovo vegetarian",
"primal",
"whole 30",
"vegan"
],
"occasions": [

],
"analyzedInstructions": [
{
"name": "",
"steps": [
{
"number": 1,
"step": "Roughly chop the beet, carrots and oranges. When peeling and chopping the beet, use gloves and apron to avoid staining your hands and clothes.Throw them in a juicer and in about 2 minutes you will end up with this delicious healthy juice.",
"ingredients": [
{
"id": 11124,
"name": "carrot",
"localizedName": "carrot",
"image": "sliced-carrot.png"
},
{
"id": 9200,
"name": "orange",
"localizedName": "orange",
"image": "orange.png"
},
{
"id": 1019016,
"name": "juice",
"localizedName": "juice",
"image": "apple-juice.jpg"
},
{
"id": 11080,
"name": "beet",
"localizedName": "beet",
"image": "beets.jpg"
}
],
"equipment": [
{
"id": 404683,
"name": "juicer",
"localizedName": "juicer",
"image": "juicer.jpg"
}
],
"length": {
"number": 2,
"unit": "minutes"
}
}
]
}
],
"spoonacularSourceUrl": "https://spoonacular.com/immunity-booster-beet-carrot-orange-juice-1095729",
"usedIngredientCount": 0,
"missedIngredientCount": 3,
"missedIngredients": [
{
"id": 11080,
"amount": 1.0,
"unit": "",
"unitLong": "",
"unitShort": "",
"aisle": "Produce",
"name": "beet",
"original": "1 Beet, peeled",
"originalName": "Beet, peeled",
"meta": [
"peeled"
],
"image": "https://spoonacular.com/cdn/ingredients_100x100/beets.jpg"
},
{
"id": 11124,
"amount": 1.0,
"unit": "pound",
"unitLong": "pound",
"unitShort": "lb",
"aisle": "Produce",
"name": "carrots",
"original": "1 pound Carrots, peeled",
"originalName": "Carrots, peeled",
"meta": [
"peeled"
],
"image": "https://spoonacular.com/cdn/ingredients_100x100/sliced-carrot.png"
},
{
"id": 9200,
"amount": 2.0,
"unit": "pounds",
"unitLong": "pounds",
"unitShort": "lb",
"aisle": "Produce",
"name": "oranges",
"original": "2 pounds Oranges, peeled",
"originalName": "Oranges, peeled",
"meta": [
"peeled"
],
"image": "https://spoonacular.com/cdn/ingredients_100x100/orange.png"
}
],
"likes": 0,
"usedIngredients": [

],
"unusedIngredients": [

]
}
],
"offset": 0,
"number": 1,
"totalResults": 40
}

型号

data class ResultX(
@SerializedName("aggregateLikes")
val aggregateLikes: Int,
@SerializedName("cheap")
val cheap: Boolean,
@SerializedName("dairyFree")
val dairyFree: Boolean,
@SerializedName("extendedIngredients")
val extendedIngredients: List<ExtendedIngredient>,
@SerializedName("glutenFree")
val glutenFree: Boolean,
@SerializedName("id")
val id: Int,
@SerializedName("image")
val image: String,
@SerializedName("likes")
val likes: Int,
@SerializedName("readyInMinutes")
val readyInMinutes: Int,
@SerializedName("servings")
val servings: Int,
@SerializedName("sourceName")
val sourceName: String,
@SerializedName("sourceUrl")
val sourceUrl: String,
@SerializedName("summary")
val summary: String,
@SerializedName("title")
val title: String,
@SerializedName("vegan")
val vegan: Boolean,
@SerializedName("vegetarian")
val vegetarian: Boolean,
@SerializedName("veryHealthy")
val veryHealthy: Boolean
)
data class ExtendedIngredient(
@SerializedName("amount")
val amount: Double,
@SerializedName("consistency")
val consistency: String,
@SerializedName("image")
val image: String,
@SerializedName("name")
val name: String,
@SerializedName("original")
val original: String,
@SerializedName("unit")
val unit: String
)

API接口

@GET("/recipes/complexSearch")
suspend fun getRecipes(
@QueryMap queries: Map<String, String>
): List<ResultX>

资源类别和API存储库实现

sealed class Resource<T>(val data: T? = null, val message: String? = null){
class Success<T>(data: T): Resource<T>(data)
class Error<T>(message: String?, data: T? = null): Resource<T>(data, message)
class Loading<T>(data: T? = null): Resource<T>(data)
}
interface FoodApiRepository {
fun getRecipes(queries: Map<String, String>): Flow<Resource<List<ResultX>>>
}
class FoodApiRepositoryImpl @Inject constructor(
private val foodApi: FoodApi
): FoodApiRepository {
override fun getRecipes(queries: Map<String, String>): Flow<Resource<List<ResultX>>> {
return flow {
try {
emit(Resource.Loading())
val result = foodApi.getRecipes(queries = queries)
emit(
Resource.Success(
data = result
)
)
}catch (e: HttpException){
emit(Resource.Error(e.localizedMessage ?: "An expected error occurred"))
}catch (e: IOException){
emit(Resource.Error("Couldn't reach server. Check your internet connection"))
}
}
}
}

食物列表状态类别和主页视图模型

data class FoodListState(
val isLoading: Boolean = false,
val recipes: List<ResultX> = emptyList(),
val error: String = ""
)
@HiltViewModel
class HomeViewModel @Inject constructor(
private val foodApiRepository: FoodApiRepository
): ViewModel(){
private val _state = mutableStateOf(FoodListState())
val state: State<FoodListState> = _state
init {
getRecipes(applyQueries())
}
private fun getRecipes(queries: Map<String, String>) {
foodApiRepository.getRecipes(queries = queries).onEach { result ->
when(result) {
is Resource.Success -> {
_state.value = FoodListState(recipes = result.data ?: emptyList())
}
is Resource.Error -> {
_state.value =
FoodListState(
error = result.message ?: "An un expected error occurred"
)
Log.d("TAG", "getFoodRecipes: ERROR")
}
is Resource.Loading -> {
_state.value = FoodListState(isLoading = true)
}
}
}.launchIn(viewModelScope)
}
private fun applyQueries(): HashMap<String, String> {
val queries: HashMap<String, String> = HashMap()
queries["number"] = "50"
queries["apiKey"] = API_KEY
queries["type"] = "snack"
queries["diet"] = "vegan"
queries["addRecipeInformation"] = "true"
queries["fillIngredients"] = "true"
Log.d("TAG", "applyQueries: $queries")
return queries
}
}

主屏幕

@Composable
fun HomeScreen(
navController: NavController,
homeViewModel: HomeViewModel = hiltViewModel()
) {
val state = homeViewModel.state.value
Box(modifier = Modifier.fillMaxSize()){
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(state.recipes) { recipe ->
Text(
text = recipe.title,
style = MaterialTheme.typography.subtitle1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
)
}
}
if (state.error.isNotBlank()){
Text(
text = state.error,
color = MaterialTheme.colors.error,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp)
.align(
Alignment.Center
)
)
}
if (state.isLoading){
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
}

谢谢

您解析错误。您的"改装"函数表示它返回一个列表。返回的JSON不是这样的,而是返回一个JSON对象,而不是JSON数组。您需要另一个类ResultWrapper,该类中有一个名为result的ResultX列表,并让Reform API返回该列表。

相关内容

最新更新