May 5, 2018
development

In many programing languages using JSON (serializing and deserializing values) is an every day thing, and it can be done easily :

import json
json_string = json.dumps([1, 2, 3, {4: 5, 6: 7}])
# json_string -> ‘[1,2,3,{"4":5,"6":7}]’
json_object = json.loads(json_string)
# json_object -> [1, 2, 3, {‘4’: 5, ‘6’: 7}]

Furthermore, in C# using Json.NET, I’m able to quickly deserialize json to an object model and from an object to a json string and get all the benefits of static types:

public class Product {
    public string Name { get; set; }
    public DateTime Expiry { get; set; }
    public List<string> Sizes { get; set; }
}
var product = new Product {
    Name = "Apple",
    Expiry = new DateTime(2008, 12, 28),
    Sizes = new string[] { "Small" }
};
string json = JsonConvert.SerializeObject(product);
// {
// "Name": "Apple", "Expiry": "2008–12–28T00:00:00",
// "Sizes": ["Small"]
// }
var newProduct = JsonConvert.DeserializeObject<Product>(json);

One line de/serialization

In both instances, JSON de/serialization is a one liner, and I belive this is how it should be in every language! That is why I was so surprised to find out how verbose you have to be to de/serialize things in java and scala. There are a host of options and libraries, but in most instances, you have to deal with intermediate ASTs, custom serializers, implicit objects, and a ton of boilerplate. Most of these issues are due to java’s type erasure “which replaces all generic types with their bounds or objects”. This means that there are no real generics at runtime as all generic types are “erased.”

All I want in a JSON library is to be able to de/serialize JSON to Scala case classes and Java POJOs in a single line and with minimal setup

JsonExtensions

Despite trying many JSON libraries for java and scala, they all fall short in one way or another. That is why I decided to create JsonExtensions, a lightweight wrapper around json4s JsonExtensions hides all the boilerplate and enables me to de/serialize values just like I would in other languages. This is an example of what you can do with it:

import io.onema.json.Extensions._
case class TestJsonFoo(name: String, value: String, id: Int = 0)
val fooJson = """{"name": "test", "value": "foo"}"""
val fooObject = fooJson.jsonDecode[TestJsonFoo]
//fooObject.name should be("test")
//fooObject.value should be("foo")
//fooObject.id should be(0)
val result = fooObject.asJson
// {"name": "test", "value": "foo", id: 0}

For java POJOs you have to import a different set of extensions, but the code remains the same:

// TestJsonPojo.java
public class TestJsonPojo {
    private String id;
    private String name;
    private String value;
    public String getId () {return id;}
    public void setId (String id) {this.id = id;}
    public String getName () {return name;}
    public void setName (String name) {this.name = name;}
    public String getValue () {return value;}
    public void setValue (String value) {this.value = value;}
    
    @Override
    public String toString() {
        return "TestJsonPojo [id = "+id+", name = "+name+", value = "+value+"]";
    }
}

// TestJavaExtensions.scala
import io.onema.json.JavaExtensions._

val fooObject = fooJson.jsonDecode[TestJsonPojo]
//fooObject.getName should be("test")
//fooObject.getValue should be("foo")
//fooObject.getId should be(0)
val result = fooObject.asJson
// {"name": "test", "value": "foo", id: 0}