Unable to read properties from data object in Kotlin/JS

Hello everyone,

I am currently developing a Kotlin/JS application for an internal demo, and I’m using the fetch API to call a back-end service that provides weather information. I’ve written the following code to make the API call:

fun weatherApi() = window.fetch("http://localhost:9090/weather")
    .then { response -> response.json() }
    .then { response -> JSON.stringify(response) }

data class CurrentWeather(
    var temperature: Double = 0.0,
    var windspeed: Double = 0.0,
    var winddirection: Int = 0,
    var is_day: Int = 0,
    var time: String = ""
)

val Weather = FC<Props> {
    val city by useState("Hamburg")
    var temperature by useState(0.0)

    useEffect {
        weatherApi().then { response ->
            val currentWeather = JSON.parse<CurrentWeather>(response)
            temperature = Object.entries(currentWeather).find { key -> key.component1() == "temperature" }?.component2() as Double
        }.catch { error ->
            console.error(error)
        }
    }

    div {
        + "The weather in $city is currently $temperature degrees."
    }
}

When I try to read the temperature value directly from the CurrentWeather object using currentWeather.temperature, I encounter the following error:

TypeError: currentWeather.get_temperature_j7qf4b_k$ is not a function

In order to retrieve the value from the CurrentWeather object, I have to use the following code:

temperature = Object.entries(currentWeather).find { key -> key.component1() == "temperature" }?.component2() as Double

I believe I’m missing some information on how to properly parse the JSON response. I have tried referring to the documentation but haven’t been able to find a solution. Could you please help me resolve this issue and correctly parse the JSON response?

Thank you in advance for your assistance.

JSON.parse creates a new object with the fields it found. The created object:

  • does not belong to the original class
  • does not have any of the methods of the original class (e.g. no getters)
  • does not extend any of the interfaces or abstract classes its original class extends

This is a common problem in the JS world too: Parsing JSON from Typescript restores data members but not type: cannot call methods on result - Stack Overflow.

Solution 1: manual hydration

// Let's imagine this is the class you want to put in the JSON
// You'll need to do this for *all* classes that become JSON
data class Point(val x: Int, val y: Int)

// To JSON
val json = JSON.stringify(Point(1, 2))

// From JSON
val deserialized = JSON.parse<Point>(json)
val result = Point(deserialized.x, deserialized.y) // force the creation of an instance of the class using the constructor

Note that if you go for this solution, you must call the constructor for all objects in the JSON, recursively, even for things like String. JSON.parse only behaves as you expect for JS primitives (Int and smaller should be fine, Float and Double should be fine, Boolean should be fine—that’s it). Any object or class must be instantiated manually.

Solution 2: don’t use JSON.parse

This problem is specific to JSON.stringify/JSON.parse. Another solution is to use something else, for example KotlinX.Serialization.

@Serializable
data class Point(val x: Int, val y: Int)

// To JSON
val json = Json.encodeToString(Point(2, 3))

// From JSON
val result = Json.decodeFromString<Point>(json)

KotlinX.Serialization knows what class the object came from, and is able to recreate it exactly as you’d expect.

2 Likes

@clovisai Thank you for your response! The solution 2 worked for me.