Skip to content

A concise and lightweight Kotlin DSL to build JSON objects and render their String representations

License

Notifications You must be signed in to change notification settings

lectra-tech/koson

Repository files navigation

Koson Logo

Koson

Deploy koson koson coverage Maven Central awesome kotlin badge

A concise and lightweight Kotlin DSL to build JSON objects and render their String representations

Using no other dependency than Kotlin

Available on Maven Central

Now part of Awesome Kotlin !

Live

DSL

// for an empty object
obj { }

// for an object
obj {
  "attribute" to 42
  "another" to true
}

// for an empty array
arr

// for an array
arr["element", 12.345, null]
Note
Any combination of these elements is possible

How to use

Kotlin
val obj = obj {
  "key" to 3.4
  "anotherKey" to arr["test", "test2", 1, 2.433, true]
  "nullValue" to null
  "emptyObject" to obj { }
  "emptyArray" to arr
  "custom" to ZonedDateTime.now()
}

println(obj) (1)
println(obj.pretty()) (2)
JSON output
(1)
{"key":3.4,"anotherKey":["test","test2",1,2.433,true],"nullValue":null,"emptyObject":{},"emptyArray":[],"custom":"2020-07-15T10:59:19.042965+02:00[Europe/Paris]"}

(2)
{
  "key": 3.4,
  "anotherKey": [
    "test",
    "test2",
    1,
    2.433,
    true
  ],
  "nullValue": null,
  "emptyObject": {

  },
  "emptyArray": [

  ],
  "custom": "2020-07-15T10:59:19.042965+02:00[Europe/Paris]"
}
Kotlin
val array = arr[
  "example",
  obj {
    "apple" to "pie"
    "key" to 3.14
     "anotherKey" to arr["first", "second", 1, 2.433, true]
  }
]

println(array) (1)
println(array.pretty()) (2)
JSON output
(1)
["example",{"apple":"pie","key":3.14,"anotherKey":["first","second",1,2.433,true]}]

(2)
[
  "example",
  {
    "apple": "pie",
    "key": 3.14,
    "anotherKey": [
      "first",
      "second",
      1,
      2.433,
      true
    ]
  }
]

Inlining obj entries

If you wish so, you can inline multiple entries in obj using Kotlin’s optional statement separator ; semicolon

obj { "key" to 3.4; "another" to true }

Alternative arr syntax

You can provide any Iterable, thus Collection to feed in an array arr

arr[listOf("element", 12.345, null)]
arr[setOf("element", 12.345, null)]

Pretty print

When using the pretty() method on obj or arr, you can define the number of whitespaces used for tabulation.Default value is 2.

// "compact", single line mode
obj { ... }

// 2 whitespaces tabulation
obj { ... }.pretty()

// 3 whitespaces tabulation
arr[ ... ].pretty(3)

Strong type constraints

  • A JSON key (attribute) can only be of Kotlin type String (will render escaping ", \ and \n chars)

Note

In IntelliJ, a compilation error will appear, and code will be flagged as not reachable

Koson Typing

  • A JSON value of an obj { } or an arr[…​] can be one of the following Kotlin or Koson DSL instances

    • obj { }

    • arr[…​]

    • arr (empty array)

    • String? (will render escaping ", \ and \n chars)

    • Number?

    • Boolean?

    • null

    • Any? (will render using .toString(), escaping ", \ and \n chars)

    • YourType : CustomKoson, will render using .serialize(). (See next section for details)

    • rawJson("{…​}") (will render as is). (See section below)

Custom Types Serialization

If you don’t want to rely on the default toString() that would be used on an Any?, you can provide an instance that implements the CustomKoson interface.

You’ll then need to override the serialize(): KosonType method.

Tip
The KosonType return type should either be an obj or an arr
Example
class Person(
    val firstName: String,
    val lastName: String,
    val age: Int
) : CustomKoson {
    override fun serialize() = obj {
        "usualName" to "$firstName $lastName"
        "age" to age
    }
}

Raw Json

If you know what content will be rendered upfront, you should favor using DSL elements to build your Json.

However, sometimes you may need to include an external source of Json. You can use the rawJson(validJson: String?) method to do so.

obj {
  "rawContent" to rawJson(externalJsonSource())
}
Warning

You need to ensure the parameter String? is a valid Json (by format), otherwise the generated Json will not be valid.

Beware that rendering will not escape ", \ and \n chars, on purpose.

Note
pretty() will not work on rawJson Strings, however toString() will inline provided content

Runtime prerequisites

  • Kotlin

  • Java 1.8 or later

Build prerequisites

  • Java 8 or later

./mvnw package

Benchmarks

Benchmarks have been conducted with the jmh OpenJDK tool.Benchmark project can be found under benchmarks folder.

Two tests were done with the same objects and arrays

  • Rendering a big object (String representation)

  • Rendering a big array (String representation)

Koson was put side to side with one of the most popular JSON builder for Java : JSON-java

Testing environment : 3.3 GHz Intel Core i5-6600, 4 cores, VM version: OpenJDK 11.0.1, 64-Bit Server VM, 11.0.1+13

Table 1. Score in operations/second (throughput mode), higher = better
Benchmark Score Error Units

BigObject - JSON-java

17120,661

± 45,741

ops/s

BigObject - Koson

17433,982

± 372,361

ops/s

BigObject (pretty) - JSON-java

8902,486

± 19,417

ops/s

BigObject (pretty) - Koson

10252,254

± 71,377

ops/s

BigArray - JSON-java

15272,946

± 139,435

ops/s

BigArray - Koson

14816,130

± 132,266

ops/s

BigArray (pretty) - JSON-java

7744,935

± 41,067

ops/s

BigArray (pretty) - Koson

8607,388

± 31,712

ops/s

To run the tests locally with java 8 or later, do

cd benchmarks
mvn package
java -jar target/benchmarks.jar