Skip to content

kittinunf/Forge

Repository files navigation

Forge

Kotlin jcenter MavenCentral Build Status Codecov

Forge is a JSON library that helps you map your Kotlin class from a JSON and vice versa in a functional way. Forge is highly inspired by Aeson, JSON parsing library in Haskell.

Ideology

Have you ever wonder that how other JSON libraries out there work? Magic under the hood? or a complex annnotation processing? If that is something that you don't want, with Forge, we don't do any of those.

Forge aims to provide a full control over how to parse JSON into a Kotlin class, no more magic, no more annotation.

Installation

Gradle

repositories {
    jcenter() //or mavenCentral()
}

dependencies {
    compile 'com.github.kittinunf.forge:forge:<latest-version>'
}

Usage (tl;dr:)

Given you have JSON as such

{
  "id": 1,
  "name": "Clementina DuBuque",
  "age": 46,
  "email": "[email protected]",
  "phone": {
    "name": "My Phone",
    "model": "Pixel 3XL"
  },
  "friends": [
    {
        "id": 11,
        "name": "Ervin Howell",
        "age": 32,
        "email": "[email protected]",
        "phone": {
            "name": "My iPhone",
            "model": "iPhone X"
        },
        "friends": []
    }
  ],
  "dogs": [
    {
      "name": "Lucy",
      "breed": "Dachshund",
      "is_male": false
    }
  ]
}

You can write your Kotlin class definition as such

data class User(val id: Int,
                val name: String,
                val age: Int,
                val email: String?,
                val phone: Phone,
                val friends: List<User>,
                val dogs: List<Dog>?)

data class Phone(val name: String, val model: String)
data class Dog(val name: String, val breed: String, val male: Boolean)

fun userDeserializer(json: JSON) =
    ::User.create.
        map(json at "id").
        apply(json at "name").
        apply(json at "age").
        apply(json maybeAt "email").
        apply(json.at("phone", phoneDeserializer)),  // phoneDeserializer is a lambda, use it directly
        apply(json.list("friends", ::userDeserializer)).  //userDeserializer is a function, use :: as a function reference
        apply(json.maybeList("dogs", dogDeserializer))

val phoneDeserializer = { json: JSON ->
    ::Phone.create.
        map(json at "name").
        apply(json at "model")
}

val dogDeserializer = { json: JSON ->
    ::Dog.create.
        map(json at "name").
        apply(json at "breed").
        apply(json at "is_male")
}

Viola!, then, you can deserialize your JSON like

//jsonContent is when you receive data as a JSON
val result = Forge.modelFromJson(jsonContent, ::userDeserializer)

when (result) {
    DeserializedResult.Success -> {
        val user = result.value
        //success, do something with user
    }

    DeserializedResult.Failure -> {
        val error = result.error
        //failure, do something with error
    }
}

You can also serialize you Kotlin class definition as JSON such:

fun userSerializer(user: User) = JSON.Object(mapOf(
    "id" to user.id.toJson(),
    "name" to user.name.toJson(),
    "age" to user.age.toJson(),
    "email" to user.email.maybeJson(),
    "friends" to user.friends.toJson(::userSerializer),
    "dogs" to user.dogs.maybeJson(::dogSerializer)
))

fun dogSerializer(dog: Dog) = JSON.Object(mapOf(
    "name" to dog.name.toJson(),
    "breed" to dog.breed.toJson(),
    "male" to dog.male.toJson()
))

val json = Forge.jsonFromModel(model, ::userSerializer)

json.asString() 
/* ==>
{
  "id": 1,
  "name": "Clementina DuBuque",
  "age": 46,
  "email": "[email protected]",
  "friends": [
    {
        "id": 11,
        "name": "Ervin Howell",
        "age": 32,
        "email": "[email protected]",
        "friends": []
    }
  ],
  "dogs": [
    {
      "name": "Lucy",
      "breed": "Dachshund",
      "is_male": false
    }
  ]
}
*/

Credits

Forge is brought to you by contributors.

Licenses

Forge is released under the MIT license.