Home > OS >  How to parse json array of objects inside object using Retrofit
How to parse json array of objects inside object using Retrofit

Time:02-11

I am trying to parse JSON (https://raw.githubusercontent.com/Biuni/PokemonGO-Pokedex/master/pokedex.json) to show data in RecyclerView, but I get an error:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.myapplication, PID: 13534
    java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $
        at com.google.gson.stream.JsonReader.beginArray(JsonReader.java:350)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:80)
        at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:39)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:27)
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:243)
        at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:153)
        at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
        at java.lang.Thread.run(Thread.java:919)

I see that the problem is that JSON contains just 1 object (pokemon) and inside this object there is array of different pokemons. I don't know how to parse array inside the upper level object in JSON. What should I change to make it work? I suppose that I should save this pokemon object and then parse it but I don't know hot to get inside it. Thanks.

API interface:

interface SimpleApi {
    @GET("pokedex.json")
    suspend fun getCustomPosts(): Response<List<Post>>
}

Repository:

class Repository {
    suspend fun getCustomPosts(): Response<List<Post>>{
        return RetrofitInstance.api.getCustomPosts()
    }
}

ViewModel:

class MainViewModel(val repository: Repository) : ViewModel() {

    val myCustomPosts = MutableLiveData<Response<List<Post>>>()

    fun getCustomPosts() {
        viewModelScope.launch {
            val response: Response<List<Post>> = repository.getCustomPosts()
            myCustomPosts.value = response
        }
    }
}

MainActivity:

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel
    private lateinit var binding: ActivityMainBinding
    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: MyAdapter


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        val view = binding.root
        setContentView(view)
        setRecyclerView()

        val repository = Repository()
        val viewModelFactory = MainViewModelFactory(repository)
        viewModel = ViewModelProvider(this, viewModelFactory).get(MainViewModel::class.java)

        viewModel.getCustomPosts()

        viewModel.myCustomPosts.observe(this, Observer { response ->
            if (response.isSuccessful) {
                response.body()?.let { adapter.setData(it) }
            } else {
                Toast.makeText(this, response.code(), Toast.LENGTH_SHORT).show()
            }
        })
    }

    private fun setRecyclerView() {
        adapter = MyAdapter()
        recyclerView = binding.recyclerView
        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = adapter
    }
}

RecyclerView Adapter:

class MyAdapter() : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    private var postList = emptyList<Post>()

    class MyViewHolder(view: View) : RecyclerView.ViewHolder(view) {
        val idView: TextView = view.findViewById(R.id.id_txt)
        val nameView: TextView = view.findViewById(R.id.name_txt)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(
            LayoutInflater.from(parent.context).inflate(R.layout.row_layout, parent, false)
        )
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val currentItem = postList[position]
        holder.idView.text = currentItem.id.toString()
        holder.nameView.text = currentItem.name
    }

    override fun getItemCount(): Int = postList.size


    fun setData(newList: List<Post>){
        postList = newList
        notifyDataSetChanged()
    }
}

Post:

data class Post(
    @SerializedName("id")
    val id: Int,
    @SerializedName("name")
    val name: String
)

CodePudding user response:

Try to use next class in response object:

data class PokedexResponse (
    @SerializedName("pokemon")
    val pokemons: List<Post>
)

interface SimpleApi {
    @GET("pokedex.json")
    suspend fun getCustomPosts(): Response<PokedexResponse>
}

My guess is that you missed to parse pokemon object:

{
  "pokemon": [{ ... }]
}
  • Related