DEV Community

loading...

Let's write a tiny JSON parser in Kotlin! Part 2: The Parser

Will BL
Game Modder and Software Developer
・2 min read

So now we have an understanding of JSON and the components that make it up, but we have to create something that will read it - this will be our parser.

Our parser will step through our JSON file character by character to read it, so it'll need to:

  • remember our JSON
  • remember where in the JSON it's currently looking
  • know if it's at the end
  • read the current character
  • step to the next character

So let's do it!
First, we'll create a class which will take in our JSON as a CharSequence (we could use a String, too - the functionality is the same, but this lets you do something weird like throw a StringBuilder in there), and will remember where it's currently reading:

class Parser(private val input: CharSequence) {
    var cursor: Int = 0
}
Enter fullscreen mode Exit fullscreen mode

Now let's give it the ability to read and step through the input.

class Parser(private val input: CharSequence) {
    var cursor: Int = 0

    private fun step(): Char {
        return input[cursor++]
    }

    private fun peek(): Char {
        return input[cursor]
    }

    private fun skip() {
        cursor++
    }
}
Enter fullscreen mode Exit fullscreen mode

peek() will read the current character, skip(), will move the cursor forwards by one, and step() and read and then move on.

However, trying to read past the end will throw an exception, so let's add a method to check if we're at the end:

    private fun hasNext(): Boolean {
        return cursor < input.length
    }
Enter fullscreen mode Exit fullscreen mode

This is everything we'll need for moving along our JSON text. Next, let's add some functions to actually read it:

fun read(predicate: (Char) -> Boolean): String {
    val result = StringBuilder()
    while (predicate(peek())) {
        result.append(step())
    }
    return result.toString()
}

fun read(pattern: CharSequence): String {
    val result = StringBuilder()
    var patternCursor = 0
    while (peek() == pattern[patternCursor++]) {
        result.append(skip())
    }
    return result.toString()
}
Enter fullscreen mode Exit fullscreen mode

These two functions are vital to how the parser will do its job:

They read the JSON one character at a time, and store what they've read in a buffer, until the character they read reaches a certain condition. For the first function, that condition is given via a predicate, and in the second, the condition is that the buffer's contents match a given string. Once the condition is met, the buffer's contents are returned.

These five methods, simple as they seem, are extremely powerful, and all we need as a foundation for our parser!

Discussion (0)