Skip to content

konnik/kotlin-json-parser

Repository files navigation

Kotlin JSON parser (from scratch)

UPDATE: now also includes some nice decoder combinators

Java CI with Gradle

This is a tiny but fully functional JSON parser I hacked together in Kotlin in a day just for the fun of it. One of the inspirations was Graham Hutton's nice introduction to functional parsing on YouTube.

My main goal was to build a fp-style recursive descent parser exploring Kotlin's support for functional programming techniques and I do like how it turned out. I think the code is pretty readable and the different parsers for the grammar reads almost exactly as the corresponding grammar rules on www.json.org.

Note that I have not made any effort on making it perform well and I haven't benchmarked it at all. It's probably pretty slow, it's just a toy project after all...

The source is in a single file and under 200 lines (not counting comments and blanks) and can easily be copied into your own project if you want to use it.

Suggestions are welcome if you think something can be done in a better way.

Please also check out my other JSON parsers written in Haskell and Clojure.

Happy parsing!

Now with decoder combinators

After finishing the parser I really wanted to have a go at implementing something similar to the Elm JSON Decode package.

I really like the Elm way of decoding JSON by describing the expected data structure using decoder combinators and mapping the data to the applications domain model.

My implementation have almost all the features of the Elm package and I think it turned out to be quite nice.

For example consider the following two JSON objects describing two different types of users, guests and registered users:

Guest:

{
  "type": "guest",
  "displayName": "Guest123"
}

Registered user:

{
  "type": "registered",
  "id": 42,
  "alias": "mrsmith",
  "email": "mrsmith@example.com",
  "phone": null
}

Let's say we want to decode guests and registered users into the following data structure:

sealed interface User {
    data class Guest(
        val displayName: String
    ) : User
    data class Registered(
        val id: Int,
        val alias: String,
        val email: String,
        val phone: String?
    ) : User
}

First we need to define a Decoder for User objects like this:

val userDecoder: Decoder<User> =
    (field("type") of str).andThen { type ->
        when (type) {
            "guest" -> map(
                field("displayName") of str,
                User::Guest
            )

            "registered" -> map(
                field("id") of int,
                field("alias") of str,
                field("email") of str,
                field("phone") of nullable(str),
                User::Registered
            )

            else -> fail("Invalid type: $type")
        }
    }

We can now use userDecoder to parse and decode the JSON data using the decodeJson function:

val guest = decodeJson(guestJson, userDecoder)
println(guest)

val registered = decodeJson(registeredJson, userDecoder)
println(registered)

This will render the output:

Ok(value=Guest(displayName=Guest123))
Ok(value=Registered(id=42, alias=mrsmith, email=mrsmith@example.com, phone=null))

Note that I had to implement a simple Result type too, of course also heavily inspired by the Result type in Elm.

About

Simple recursive descent parser for JSON

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages