jsonandroid-studiokotlinanko

How to parse an irregular JSON string from an API


Hello I am new to Android Studio Kotlin, and I'm stuck in this issue: I have a JSON string returned by OpenWeatherMap API.A similar question was presented in the past link, but it was Java.

This is a typical string:

 {
    "coord": {
        "lon": -2.93,
        "lat": 43.26
    },
    "weather": [{
        "id": 801,
        "main": "Clouds",
        "description": "few clouds",
        "icon": "02n"
    }],
    "base": "stations",
    "main": {
        "temp": 60.69,
        "feels_like": 62.33,
        "temp_min": 59,
        "temp_max": 63,
        "pressure": 1023,
        "humidity": 100
    },
    "visibility": 10000,
    "wind": {
        "speed": 3.36,
        "deg": 120
    },
    "clouds": {
        "all": 20
    },
    "dt": 1602195029,
    "sys": {
        "type": 1,
        "id": 6395,
        "country": "ES",
        "sunrise": 1602224311,
        "sunset": 1602265138
    },
    "timezone": 7200,
    "id": 3128026,
    "name": "Bilbao",
    "cod": 200
}

And this is the code I have:

package com.example.downloadtest

import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import org.jetbrains.anko.doAsync
import org.jetbrains.anko.uiThread
import org.json.JSONObject
import java.net.URL
import java.util.concurrent.Executors

val executor = Executors.newScheduledThreadPool(5)



class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        doAsync(executorService = executor) {
            //val result = URL("https://httpbin.org/get").readText()
            val result = URL("https://api.openweathermap.org/data/2.5/weather?q=Bilbao,spain&units=imperial&APPID=-------------").readText()
            val textView = findViewById(R.id.txtView) as TextView
            uiThread {
                //textView.setText(result)
                println(result)
                //toast(result)
                val data = StringBuilder()
                val jsonObject = JSONObject(result)
                val jsonArray = jsonObject.optJSONArray("weather")
                println(jsonArray)
                for (i in 0 until jsonArray.length()){
                    val jsonObject = jsonArray.getJSONObject(i)
                    val main = jsonObject.optString("main").toString()
                    val desc = jsonObject.optString("description").toString()
                    val icon = jsonObject.optString("icon").toString()
                    data.append("Bilbao Weather: \n").append(main).append(" : ").append(desc).append(" : ").append(icon)
                    textView.text = data.toString()
                }
            }
        }

    }

}

As you can see I can parse a portion of the string, but I cannot parse anything else. If I wanted to parse the "country" or the "name", how would I go about it?

My thanks in advance of any help.

Ray.


Solution

  • Your json is composed of nested objects. You can't access the inside of an object without accessing the object itself.

    If you want to access the description param, you first need to make weather an object. Same for the country you need an object to be created from sys.

    You would do it like this:

    val sys: JSONObject? = jsonObject.optJSONObject("sys") //this can be null (I don't know how consistent is that model)
    val country = sys?.optString("country") ?: "" //if sys is null, default to empty string
    

    As for the name, that is a property of the initial object, thus it can be easily accessed like this:

    val name = jsonObject.optString("name")
    

    A thing to consider is: why are you doing .toString() on a String?

    The function optString("fieldName", "fallback") will always return a String, if you provide a fallback it will use that, otherwise it will return an empty String

    You should also take into consideration (on the long run) if you want to create an object that models that json, and map it with jackson or any other library, then you would access them like any other java/kotlin parameters.