Kotlin JSON Parsing that infers type 🚀
JSON.kt
is a kotlin wrapper of java's org.json.JSONObject
that exposes a nicer syntax for kotlin code.
car::id < json["id"]
car::name < json["name"]
car::statistics < json["stats", CarStatsJSONMapper()]
car::productionStartDate < json["dates.production_dates.start_date"]
Because parsing JSON is often full of unecessary if statements, obvious casts and nil-checks We deserve better !
By using a simple <
operator that takes care of the boilerplate code for us thanks to beautiful kotlin generics.
JSON mapping code becomes concise and maintainable ❤️
- Infers types
- Leaves your models clean
- Handles custom & nested models
- Pure Kotlin, Simple & Lightweight
Given a kotlin Model Car
class Car {
var id = 0
var name = ""
var statistics = CarStatistics()
var productionStartDate = 0
}
And the following JSON
file
{
"id": 265,
"name": "Ferrari",
"stats": {
"maxSpeed": 256,
"numberOfWheels": 4
},
"dates": {
"production_dates": {
"start_date": "1984",
"end_date": "1993"
}
}
}
if (jsonObject.has("id")) {
car.id = jsonObject.getInt("id")
}
if (jsonObject.has("name")) {
car.name = jsonObject.getString("name")
}
if (jsonObject.has("stats")) {
val statsParser = CarStatsJSONMapper()
car.statistics = statsParser.parse(jsonObject.getJSONObject("stats"))
}
if (jsonObject.has("dates")) {
val dates = jsonObject.getJsonObject("dates")
if (dates.has("production_dates")) {
val productionDates = dates.getJsonObject("production_dates")
if (productionDates.has("start_date")) {
car.productionStartDate = productionDates.getJsonObject("start_date")
}
}
}
car::id < json["id"]
car::name < json["name"]
car::statistics < json["stats", CarStatsJSONMapper()]
car::productionStartDate < json["dates.production_dates.start_date"]
The <
operator maps a model property with a json key.
Notice that this does exactly the same as the old parsing above, meaning that if key does not exist, nothing happens and the model keeps its previous value.
On the third line, we can provide our own custom mapper which enables us to reuse the same mapping at different places. 🤓
val json = JSON("[aJSONString]") // or JSON(jsonObject)"
val car = CarJSONParser().parse(json)
In the previous examples we used json["key"]
, here we are going to use json("key")
that returns an optional value.
Perfect for providing default values while parsing!
car.id = json("id") ?: 0
car.name = json("name") ?: "unknown car"
car.statistics = json("stats", CarStatsJSONMapper()) ?: CarStatistics()
car.productionStartDate = json("dates.production_dates.start_date") ?: 0
Sometimes it might be necessary to explicitly indicate the return type.
val id: Int = json("id")
json<JSONArray>("images")?.let { imageJsonArray ->
//Do something
}
class CarJSONParser : JSONParser<Car> {
@Throws(JSONException::class)
override fun parse(json: JSON): Car {
val car = Car()
car::id < json["id"]
car::name < json["name"]
car::statistics < json["stats", CarStatsJSONMapper()]
car::productionStartDate < json("dates.production_dates.start_date")
return car
}
}
Just copy-paste JSON.kt
file \o/