2

I am new to Kotlin and I find library Klaxon to parse JSON. I can not find how to execute url(http://localhost:8080/items/2), read JSON string and save data to variables and print them to console. CreatedAt and UpdatedAt I do not need to save.

JSON from url:

{
    "brand": "Ferrero",
    "name": "Nutella",
    "healthy": false,
    "date": "2017-03-14T00:00:00.000Z",
    "id": 2,
    "createdAt": "2018-03-14T13:33:22.000Z",
    "updatedAt": "2018-03-20T21:23:44.000Z"
}

Code:

class DetailItem : AppCompatActivity()  {
    var itemId : String = ""
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_detail_item)
        itemId = intent.getStringExtra("itemId")
    }

    override fun onResume() {
        super.onResume()
        example()
    }

    private fun parse(name: String) : Any{
        val cls = Parser::class.java
        val inputStream = cls.getResourceAsStream(name)!!
        return Parser().parse(inputStream)!!
    }

    private fun example(){
        val url = "http://192.168.99.100:8080/items/" + itemId
        val obj = parse("url") as JsonObject

        val brand = obj.string("brand")
        val name = obj.string("name")
        println(brand + name)
    }
misolo89
  • 123
  • 1
  • 2
  • 13
  • Did you write any code so far? [Here's the documentation for Klaxon](https://github.com/cbeust/klaxon#objectBindingApi) – user2340612 Mar 20 '18 at 22:32
  • Sorry for my late answer. I added code. And error is: kotlin.KotlinNullPointerException – misolo89 Mar 21 '18 at 07:45
  • which line is throwing the exception? – user2340612 Mar 21 '18 at 08:08
  • at DetailItem.parse(val inputStream = cls.getResourceAsStream(name)!!) at DetailItem.example(val obj = parse("url") as JsonObject) at DetailItem.onResume(example()) – misolo89 Mar 21 '18 at 10:09
  • shouldn't `parse("url")` be `parse(url)` ? not sure if that's enough, but that seems like a problematic typo – Aaron Mar 21 '18 at 10:44
  • @Aaron, still not working – misolo89 Mar 21 '18 at 10:59
  • Then I'll have to let @user2340612 or someone else guide you through the steps, I've hardly got any kotlin experience. I suggest you edit the typo out of your question since answerers might otherwise go no further. – Aaron Mar 21 '18 at 11:01

2 Answers2

1

The issue is that method cls.getResourceAsStream(name) returns null, and the !! operator causes the NPE to be thrown.

Why does cls.getResourceAsStream(name) return null? That happens because method getResourceAsStream searches for a resource (i.e., file) with the provided name among the resources you provide in your project.

In your case, however, you want to download the JSON from a URL, so you need to first perform the download, which will give you a JSON in form of String (or something that can be converted to a String), and then parse that string with Parser().parse(yourJsonString) as JsonObject.

EDIT

Here's an example that uses OkHttp library:

import com.beust.klaxon.Klaxon
import okhttp3.Call
import okhttp3.Callback
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import java.io.IOException

fun main(args: Array<String>) {
    val url = "https://jsonplaceholder.typicode.com/posts/1"

    val client = OkHttpClient()
    val request = Request.Builder()
        .url(url)
        .build()

    println(Thread.currentThread())
    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call?, e: IOException?) {
            e?.printStackTrace()
        }

        override fun onResponse(call: Call?, response: Response) {
            if (!response.isSuccessful) {
                System.err.println("Response not successful")
                return
            }
            val json = response.body()!!.string()
            val myData = Klaxon().parse<MyData>(json)
            println("Data = $myData")
            println(Thread.currentThread())
        }

    })
    // Shutdown the executor as soon as the request is handled
    client.dispatcher().executorService().shutdown()
}

data class MyData(val title: String, val userId: Int)

The JSON string returned by the REST API is:

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipitsuscipit recusandae consequuntur expedita et cumreprehenderit molestiae ut ut quas totamnostrum rerum est autem sunt rem eveniet architecto"
}

That code prints:

Thread[main,5,main]
Data = MyData(title=sunt aut facere repellat provident occaecati excepturi optio reprehenderit, userId=1)
Thread[OkHttp https://jsonplaceholder.typicode.com/...,5,main]

Note that the onResponse callback is executed on the worker thread and not on the main one, so if you use this code in Android, you won't be able to change UI elements from there (unless you use runOnUiThread() or similar techniques)

user2340612
  • 10,053
  • 4
  • 41
  • 66
  • Is there solution how to read a json on url like I posted in question? If not, how to download url and save it as json file? – misolo89 Mar 21 '18 at 10:59
  • Sure, you need to use an http client (or something more high level like retrofit) to call your server and retrieve the response. After that you can save it to a file or parse it directly – user2340612 Mar 21 '18 at 11:52
  • I post new answer to solve my problem, but it is AsyncTask, How to rewrite it and execute it only when I want? – misolo89 Mar 21 '18 at 11:57
  • `AsyncTask` is one way to do it, and to execute it when you want you do something like `new MyAsyncTask().execute(...)`. I can provide you a more detailed answer later, in case – user2340612 Mar 21 '18 at 12:50
  • @misolo89 I added an example – user2340612 Mar 21 '18 at 22:38
1

I find this code to solve my problem. But it is AsyncTask, How to rewrite it and execute it only when I want?

inner class AsyncTaskHandleJson : AsyncTask<String, String, String>() {
        override fun doInBackground(vararg url: String?): String {

            var text: String
            var connection = URL(url[0]).openConnection() as HttpURLConnection
            try {
                connection.connect()
                text = connection.inputStream.use { it.reader().use { reader -> reader.readText() } }
            } finally {
                connection.disconnect()
            }

            return text
        }

        override fun onPostExecute(result: String?) {
            super.onPostExecute(result)
            handleJson(result)
        }
    }

    private fun handleJson(jsonString: String?) {

        val jsonObject = JSONObject(jsonString)

        val itemIdBrand = findViewById<TextView>(R.id.itemIdBrand)
        itemIdBrand.text = jsonObject.getString("brand")

        println(jsonObject.getString("brand"))
    }
misolo89
  • 123
  • 1
  • 2
  • 13
  • what do you mean by "rewrite it" and execute it only when you want ? Meanwhile you can read this well explained documentation from google for AsyncTask object : https://developer.android.com/reference/android/os/AsyncTask.html – Damiii Mar 21 '18 at 22:55
  • 1
    If I understand it right, AsyncTask is only executed when I call `MyAsyncTask().execute(...)` ? – misolo89 Mar 22 '18 at 08:43