Kotlin bindings for JSON manipulation via Gson

Related tags

JSON Kotson
Overview

DEPRECATED

This library is deprecated. You can still use it. It is not going any where.

However, it is not maintained anymore.

If you are looking for a cool, up-to-date JSON parsing library for Kotlin/JVM: here you go!

Kotson: Gson for Kotlin

Kotson enables you to parse and write JSON with Google's Gson using a conciser and easier syntax.

Kotson is a set of extension functions, meaning that it adds utility functions and syntactic sugars to Gson in Kotlin. It does not add new features to Gson nor does it creates new types. It is therefore usable on any Gson object, whether created from java or kotlin in source or library.

Install

Maven:

<dependency>
	<groupId>com.github.salomonbrys.kotson</groupId>
	<artifactId>kotson</artifactId>
	<version>2.5.0</version>
</dependency>

Gradle:

compile 'com.github.salomonbrys.kotson:kotson:2.5.0'

Table Of Contents

Usage

Creating Json

JsonObject:

import com.github.salomonbrys.kotson.*

val obj: JsonObject = jsonObject(
    "name" to "kotson",
    "creation" to Date().getTime(),
    "files" to 4
)

JsonArray:

import com.github.salomonbrys.kotson.*

val arr: JsonArray = jsonArray("one", "two", 42, 21.5)

Of course, a combination of both:

import com.github.salomonbrys.kotson.*

val obj: JsonObject = jsonObject(
    "property" to "value",
    "array" to jsonArray(
        21,
        "fourty-two",
        jsonObject("go" to "hell")
    )
)

JsonPrimitives:

import com.github.salomonbrys.kotson.*

val pi = 42.toJson()         // java: new JsonPrimitive(42);
val pf = 42.21f.toJson()     // java: new JsonPrimitive(42.21f);
val pd = 42.21.toJson()      // java: new JsonPrimitive(42.21d);
val pc = 'c'.toJson()        // java: new JsonPrimitive('c');
val pz = true.toJson()       // java: new JsonPrimitive(true);
val os = "coucou".toJson()   // java: new JsonPrimitive("coucou");

Setting custom (de)serializers

If you need to register a serializer / deserializer / InstanceCreator, you can use these "builder" APIs:

import com.github.salomonbrys.kotson.*

val gson = GsonBuilder()
    .registerTypeAdapter<MyType> {
    
        serialize {
            /*
                it.type: Type to serialize from
                it.context: GSon context
                it.src : Object to serialize
            */
        }
        
        deserialize {
            /*
                it.type: Type to deserialize to
                it.context: GSon context
                it.json : JsonElement to deserialize from
            */
        }
        
        createInstances {
            /*
                it: Type of instance to create
            */
        }
    
    }
    .registerTypeHierarchyAdapter<MyOtherType> {
        serialize { /* Same a above */ }
        deserialize { /* Same a above */ }
        createInstances { /* Same a above */ }
    }
    .create()

Of course, you can declare only a serializer, a deserializer or an instance creator. You don't have to declare all three.

You don't have to declare all your JsonSerializers, JsonDeserializers and InstanceCreators where you initialize Gson. Kotson allows you to create those type adapters in different files using the functions jsonSerializer, jsonDeserializer and instanceCreator. You will then be able to register those when creating the Gson object:

//TypeAdapters.kt
import com.github.salomonbrys.kotson.*

val personSerializer = jsonSerializer { /* Same arguments as before */ }
//Main.kt
import com.github.salomonbrys.kotson.*

val gson = GsonBuilder().registerTypeAdapter<Person>(personSerializer).create()

Setting custom Readers and Writers

Gson has another API (named Stream API) that allows to register writers (to JsonWriter) and readers (from JsonReader).
Here is an example for a simple Person class:

import com.github.salomonbrys.kotson.*

val gson = GsonBuilder()
    .registerTypeAdapter<Person> {
    
        write {
            beginArray()
            value(it.name)
            value(it.age)
            endArray()
        }
        
        read {
            beginArray()
            val name = nextString()
            val age = nextInt()
            endArray()
    
            Person(name, age)
        }
    
    }
    .create()

While a bit more complex and difficult to handle, this API is also better optimized. So if you're after performance, I recommend you use this API.

Using this API has a few drawbacks:

  • You must define both write and read
  • You cannot use a mix (serialize + read or write + deserialize).
  • In read, you cannot access the exact object type that you are deserializing to (the it.type of deserialize).

If you wish to register a nullable reader or writer, you can use registerNullableTypeAdapter instead.

You don't have to declare all your TypeAdapters where you initialize Gson. Kotson allows you to create those type adapters in different files using the functions typeAdapter and nullableTypeAdapter. You will then be able to register those when creating the Gson object:

//TypeAdapters.kt
import com.github.salomonbrys.kotson.*

val personTypeAdapter = typeAdapter<Person> {
    write { /*...*/ }
    read { /*...*/ }
}
//Main.kt
import com.github.salomonbrys.kotson.*

val gson = GsonBuilder().registerTypeAdapter<Person>(personSerializer).create()

Kotson provides no utility function for the TypeAdapterFactory interface. Because this interface defines a generic function, there is currently no other way to use it other than implementing it on a regular object or class.

Parsing JSON

Kotson provides a simple API that does not suffer from Java's type erasure. That means that whatever the output type, it will be parsed correctly and eliminates the need for TypeToken.

import com.github.salomonbrys.kotson.*

val gson = Gson()

// java: List<User> list = gson.fromJson(src, new TypeToken<List<User>>(){}.getType());
val list1 = gson.fromJson<List<User>>(jsonString)
val list2 = gson.fromJson<List<User>>(jsonElement)
val list3 = gson.fromJson<List<User>>(jsonReader)
val list4 = gson.fromJson<List<User>>(reader)

Attention: gson.fromJson<MyType> will return a non-nullable type whereas gson.fromJson<MyType?> will return a nullable type. Therefore the code gson.fromJson<MyType>("null") is correct and will throw a null-pointer exception!

A lot of Gson's APIs are relying on java.lang.reflect.Type to specify a type, but Kotlin's javaClass returns a java.lang.Class which is a Type but suffers from type erasure. To mediate this issue, Gson uses TypeToken to create java.lang.reflect.Type objects without type erasure. If you need such a Type object, you can simply use the typeToken function the same way you use the javaClass function. For example: typeToken<Map<String, List<User>>>()

Careful: the typeToken function behaves slightly differently then Gson's TypeToken class. When providing a non-specialized generic type, typeToken<List<*>> will return Class<List> (while Gson's mechanism will return a ParameterizedType). If you really need a ParameterizedType for a non-specialized generic type, you can use the gsonTypeToken function.

Browsing Json Elements

Kotson allows you to simply convert a jsonElement to a primitive, a JsonObject or a JsonArray:

import com.github.salomonbrys.kotson.*

val s = json.string // java: String s = json.getAsString();
val i = json.int    // java: int i = json.getAsInt();
val a = json.array  // java: JsonArray = json.getAsJsonArray();
val o = json.obj    // java: JsonObject = json.getAsJsonObject();

val ns = json.nullString // java: String s = json.isJsonNull() ? null : json.getAsString();
val ni = json.nullInt    // java: Integer i = json.isJsonNull() ? null : json.getAsInt();
val na = json.nullArray  // java: JsonArray = json.isJsonNull() ? null : json.getAsJsonArray();
val no = json.nullObj    // java: JsonObject = json.isJsonNull() ? null : json.getAsJsonObject();

The same APIs exist for .string, .bool, .byte, .char, .short, .int, .long, .float, .double, .number, .bigInteger, .bigDecimal, .array, .obj

Kotson provides a simple API that allows you to easily browse JsonElement, JsonObject and JsonArray:

import com.github.salomonbrys.kotson.*

// java: JsonElement components = colors.getAsJsonObject().get("orange");
val components = colors["orange"]

// java: JsonElement greenComp = components.getAsJsonArray().get(1);
val greenComp = components[1]


// java: int greenComp = json .getAsJsonObject()
//                            .getAsJsonObject("colors")
//                            .getAsJsonArray("orange")
//                            .get(1)
//                            .getAsInt();

val greenComp = json["colors"]["orange"][1].int

Mutating Json Elements

Kotson allows you to mutate a JsonObject or a JsonArray:

import com.github.salomonbrys.kotson.*

fun test() {
    val array = jsonArray("zero", "x", "two")
    array[1] = "one"
    array += "three"
    array -= "zero"

    val obj = jsonObject()
    obj["this"] = "that"
    obj += "answer" to 42
    obj -= "this"
}

Copying Json Elements

Kotson allows you to make a shallow copy (single-level copy) or a deep copy (recursive copy) of a JsonObject or a JsonArray:

import com.github.salomonbrys.kotson.*

val shallow = json.shallowCopy()
val deep = json.deepCopy()

Accessing object fields via property delegates

Kotson allows you to delegate properties to JsonObject fields:

import com.github.salomonbrys.kotson.*

class Person(public val obj: JsonObject) {
    val id: String by obj.byString               // Maps to obj["id"]
    val name: String by obj.byString("fullName") // Maps to obj["fullName"]
    val birthDate: Int by obj["dates"].byInt(0)  // Maps to obj["dates"][0]
}

Let's talk!

You've read so far ?! You're awsome! Why don't you drop by the Kotson Slack channel on Kotlin's Slack group?

Comments
  • Either none or all type parameters can be wildcard

    Either none or all type parameters can be wildcard

    I get "Either none or all type parameters can be wildcard in Map<String, Competition2>" when pass this Map class as parameter. This worked in the previous version. Without kotson gson can handle it well.

    opened by westito 9
  • Rethink name

    Rethink name

    I just wanted you to know, that the name of the library "Kotson", when pronounced, unfortunately sounds exactly like the german verb "kotzen" which means "to puke" or "to vomit".

    This makes it kinda hard to stay serious when coding with your team mates because everytime the name is mentioned, someone has to laugh. Additionally, this seems to amplify, everytime it happens :)

    opened by cypressious 9
  • Strange crash

    Strange crash

    I'm trying to deserialize a custom data-class with various members.

    I just tried gson.fromJson<MyType>(...) without any custom deserializers, but I keep getting a strange segfault (libc, not an exception that I can catch).

    When I call gson.fromJson<MyType>('{}') i get the correct type (with defaults set).

    opened by fab1an 7
  • Intermittently ignoring a custom type adaptor

    Intermittently ignoring a custom type adaptor

    Here is my code:

        OutputStreamWriter(GZIPOutputStream(FileOutputStream("financials-0.0.1.json.gz"))).use {
            GsonBuilder()
                    .registerTypeAdapter<Instant> {
                        serialize {
                            it.src.epochSecond.toJson()
                        }
                    }
                    .registerTypeAdapter<LocalDate> {
                        serialize {
                            it.src.toString().toJson()
                        }
                    }
                    .create().toJson(financials, it)
    

    For some reason, it seems to be intermittently ignoring the Instant custom serializer and serializing an Instant field to {"seconds":1452211200,"nanos":0}

    Sorry I can't reliably reproduce it, but can you think of any reason this could occur?

    opened by sanity 7
  • Kotlin library 'kotson-1.5.0.jar' has an unsupported format

    Kotlin library 'kotson-1.5.0.jar' has an unsupported format

    When I attempt to use the new versions of Kotlin and Kotson, I get the error "Kotlin library 'kotson-1.5.0.jar' has an unsupported format. Please update the library or plugin". Any idea what's going on?

    opened by UnknownJoe796 7
  • Default Values

    Default Values

    Does this support Kotlin's default values? With Gson when deserializing, if a value does not exist, it becomes null. Since this is supposed to support Kotson, does that value now get set to default value if it does not exist?

    opened by dri94 5
  • problems with proguard since v2.2.0

    problems with proguard since v2.2.0

    since v2.2.0 I get this build warnings and the build fails:

    Warning: com.github.salomonbrys.kotson.RegistrationBuilder$_registerTypeAdapter$1: can't find enclosing method 'void _registerTypeAdapter()' in program class com.github.salomonbrys.kotson.RegistrationBuilder
    Warning: com.github.salomonbrys.kotson.TypeAdapterBuilder$build$1: can't find referenced method 'kotlin.jvm.functions.Function1 access$get_readFunction$p(com.github.salomonbrys.kotson.TypeAdapterBuilder)' in program class com.github.salomonbrys.kotson.TypeAdapterBuilder
    Warning: com.github.salomonbrys.kotson.TypeAdapterBuilder$build$1: can't find referenced method 'kotlin.jvm.functions.Function2 access$get_writeFunction$p(com.github.salomonbrys.kotson.TypeAdapterBuilder)' in program class com.github.salomonbrys.kotson.TypeAdapterBuilder
    Warning: com.github.salomonbrys.kotson.TypeAdapterBuilder$build$1: can't find enclosing method 'com.google.gson.TypeAdapter build()' in program class com.github.salomonbrys.kotson.TypeAdapterBuilder
    
    opened by the4thfloor 5
  • Kotlin 1.0 Beta Candidate support

    Kotlin 1.0 Beta Candidate support

    Please, consider upgrade Kotlin version. Any code compiled with previous versions of Kotlin is not compatible with 1.0.

    http://blog.jetbrains.com/kotlin/2015/10/kotlin-1-0-beta-candidate-is-out/

    opened by peleteiro 5
  • Non-nullables in model are ignored

    Non-nullables in model are ignored

    Say, I have data class User(val clientId: Int, val firstName: String)

    Both properties can never be null, that is how it should be. But if I get JSON { clientId: null, firstName: null }, my User will have nulls in it without throwing any errors and it will break everything. Now, you might think that I should have nullable properties if my data coming in JSON may can contain them... But the design of back-end is such that if the user is not found, all the data properties are nulls. I would rather try/catch the error, like so:

     fun parseUser(response: Response): User? =
          try {
            mapper.fromJson<User>(response.body().string())
          } catch (e: Exception) {
            null
          }
    

    The idea is that I don't need the User object at all if BE returned all properties as nulls.

    Is there an elegant way to handle this situation? Note that I have much more fields in User model than I presented, all but one of them is never null, unless user is not found.

    opened by NeverwinterMoon 3
  • Stream TypeAdapters

    Stream TypeAdapters

    This is an issue to track the integration of the Stream TypeAdapter API into Kotson.

    The JsonSerializer documentation states:

    New applications should prefer TypeAdapter, whose streaming API is more efficient than this interface's tree API.

    Kotson version 2.2.0-beta1 has been pushed to Maven Central and exposes this API.

    Feel free to test it and report any concern or issue here ;)

    opened by SalomonBrys 3
  • Nested generic type Issue

    Nested generic type Issue

    Hi, just noticed an issue while using nested types e.g.

    data class User(val id: String)
    
    val a = gson.fromJson<List<List<User>>>("[\n" +
                "  [\n" +
                "    {\n" +
                "      \"id\": \"u-abcd\"\n" +
                "    }\n" +
                "  ]\n" +
                "]\n")
    

    Expecting a to be of type ArrayList<ArrayList<User>>> however I'm getting ArrayList<ArrayList<LinkedTreeMap<etc... as per Gson>>> It looks like it's not getting past the 'first' level generic type.

    opened by brianyu0717 2
  • IllegalStateException when Gson returns null

    IllegalStateException when Gson returns null

    When the Gson's native fromJson method returns null (ex. when the JSON string is empty), a

    java.lang.IllegalStateException: fromJson(json, typeToken<T>()) must not be null
    

    exception is thrown due to the extension function being defined as Gson.fromJson(json: String): T. I developed a workaround for it (note the nullable type T)

    inline fun <reified T: Any> Gson.fromJsonOrNull(json: Reader): T? = fromJson(json, typeToken<T>())
    

    I'm unsure whether this would be the correct approach, thus I'm not submitting a PR.

    opened by RouNNdeL 0
  • java.lang.IllegalArgumentException serializing list of object

    java.lang.IllegalArgumentException serializing list of object

    I'm trying convert a list of object, but when i use toJsonArray(), it gave me

    java.lang.IllegalArgumentException
            at com.github.salomonbrys.kotson.BuilderKt.toJsonElement(Builder.kt:24)
            at com.github.salomonbrys.kotson.BuilderKt._jsonArray(Builder.kt:31)
            at com.github.salomonbrys.kotson.BuilderKt.jsonArray(Builder.kt:36)
            at com.github.salomonbrys.kotson.BuilderKt.toJsonArray(Builder.kt:39)
    
    opened by michele-grifa 0
  • Simple samples of using Kotson are necessary

    Simple samples of using Kotson are necessary

    for example, this does not work

    `import com.github.salomonbrys.kotson.* import com.google.gson.*

    fun main (args: Array ) { val obj:JsonObject = jsonObject( "name" to "kotson", "files" to 4 ) println(" ${obj}") class Person(public val obj: JsonObject) { val name: String by obj.byString("name") val files: Int by obj.byInt("files")
    } val person=Gson().fromJson(obj,Person ::class.java) }`

    opened by kubanychlocal 0
  • Serializing delegates

    Serializing delegates

    It would be nice if there was a option (or if it was the default behavior) to evaluate delegates when serializing objects.

    For example, val jumps by json.byInt would serialize to something like "jumps": 3.

    This would extremely helpful when using a class that generated from json using the json delegates (byInt, etc). Right now, using these requires a custom serializer to be serializable properly.

    opened by rnett 0
  • Nested generic type in generic function

    Nested generic type in generic function

    I have a json like this

        val JSON_LIST_STRING = """
    {
      "code": "0",
      "msg": "Success",
      "subcode": "0",
      "data": [{
        "test": 10
      }
      ]
    }
    """.trimIndent()
    

    model like this

        data class TestObject(val test: Int)
    
    public class ApiResult<T> {
        private int code;
        private int subcode;
        private String msg;
        private T data;
    
        public int getCode() {
            return code;
        }
    
        public void setCode(int code) {
            this.code = code;
        }
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public T getData() {
            return data;
        }
    
        public void setData(T data) {
            this.data = data;
        }
    
        public boolean isOk() {
            return code == 0;
        }
    
        @Override
        public String toString() {
            return "ApiResult{" +
                    "code='" + code + '\'' +
                    ", msg='" + msg + '\'' +
                    ", data=" + data +
                    '}';
        }
    
        public int getSubcode() {
            return subcode;
        }
    
        public void setSubcode(int subcode) {
            this.subcode = subcode;
        }
    }
    
    

    test code like this

      @Test
        fun testFunListGson() {
    
            val result = data<List<TestObject>>()
            assertThat(result.data[0].test).isEqualTo(10)
        }
    
        @Test
        fun testDirectListGson() {
    
            val result = gson.fromJson<ApiResult<List<TestObject>>>(JSON_LIST_STRING)
            assertThat(result.data[0].test).isEqualTo(10)
        }
    
        fun <T> data(): ApiResult<T> {
            return gson.fromJson(JSON_LIST_STRING)
        }
    

    Expect testFunListGson will pass, but it failed like this

    java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.xxx.GsonTest$TestObject
    

    It looks like it's not getting past the underneath generic type.

    opened by lowwor 0
  • Using delegate to map to array of Strings

    Using delegate to map to array of Strings

    Would be nice to map directly to Array of Strings if possible. Might be missing how to do this, but this is my current way of doing so:

        private val _modes by obj.byNullableArray("MODES")
        val modes: Array<String>? by lazy {
            _modes?.map { it.string }?.toTypedArray()
        }
    
    opened by nibuen 0
Owner
Salomon BRYS
Salomon BRYS
A lightweight Kotlin DSL to render DSL style Json to String.

A lightweight Kotlin DSL to render DSL style Json to String.

null 4 May 5, 2021
Modern JSON processor with readable Kotlin syntax.

Kq Modern cross-platform JSON processor with readable Kotlin syntax. cat ~/Desktop/bdb.ndjson | kq '.filter{it.bool("muted")}.sortedBy{it.long("size")

Daniel Demidko 6 Jan 25, 2022
Manager of progress using Lottie JSON, compatible for Java and Kotlin.

Progress Lottie IGB Manager of progress using Lottie JSON, compatible for Java and Kotlin. Int Top In Bottom Important Info: To create a raw folder: R

Isaac G. Banda 21 Sep 16, 2022
Customizable JSON Schema-based forms for Kotlin and Compose

Kotlin JSON Forms Customizable JSON Schema-based forms for Kotlin and Compose This project aims to reimplement JSON Forms in Kotlin for use in Compose

Copper Leaf 3 Oct 1, 2022
Fast JSON parser for java projects

ig-json-parser Fast JSON parser for java projects. Getting started The easiest way to get started is to look at maven-example. For more comprehensive

Instagram 1.3k Dec 29, 2022
A Java serialization/deserialization library to convert Java Objects into JSON and back

Gson Gson is a Java library that can be used to convert Java Objects into their JSON representation. It can also be used to convert a JSON string to a

Google 21.7k Jan 5, 2023
xls2json - Read in Excel file (.xls, .xlsx, .xlsm) and output JSON

xls2json Read in Excel file (.xls, .xlsx, .xlsm) and output JSON. Evaluates formulas where possible. Preserve type information from Excel via JSON typ

Tammo Ippen 39 Oct 19, 2022
Simple CLI app to convert XML retrieved from a configurable URI to JSON and back

XmlToJsonUtility Simple CLI app written in Kotlin (1.5.31) on Java 11, using Spring Boot. Queries a URI (default) as an XML source. Attempts to valida

Darius Washington 2 Oct 20, 2021
ktlint JSON Lines reporter

ktlint JSON Lines reporter Usage Download the jar and run: ktlint --reporter=jsonlines,artifact=ktlint-jsonlines-reporter.jar Download Either downloa

Anton Musichin 1 Nov 24, 2021
Generate a JSON bookmarks document from a GitHub user

Github to bookmarks This little webapp will generate a JSON bookmarks document from a GitHub user. This is intended to be used with bbt. An instance i

Benoit Lubek 2 Nov 8, 2021
Kotlin extensions for Moshi, Make every thing you want with Moshi in just one line.

Kotlin extensions for Moshi, Make every thing with square / Moshi in one line.

Mazen Rashed 15 Nov 21, 2021
Pojson provides Kotlin DSL for building complex jsons in declarative manner.

Pojson is a kotlin library for json prototyping. It brings DSL for building JsonObjectPrototype and JsonArrayPrototype. Prototypes don't reference to any json object/array models.

Alexander Mironychev 5 Jan 4, 2022
Viacheslav Veselov 0 Jul 8, 2022
BetterNBT - A Gson-like API for intuitively working with Minecraft NBTs

BetterNBT A lightweight (under 250 lines of code) Kotlin library for Fabric 1.18

RedGrapefruit 1 Apr 13, 2022
An image manipulation library for Kotlin

Sketch An image manipulation library for Kotlin. Sketch doesn't require any external installation like OpenCV or OCR and can be used right away. It's

Eugene R. 40 Oct 30, 2022
🚀Plugin for Android Studio And IntelliJ Idea to generate Kotlin data class code from JSON text ( Json to Kotlin )

JsonToKotlinClass Hi, Welcome! This is a plugin to generate Kotlin data class from JSON string, in another word, a plugin that converts JSON string to

Seal 2.8k Jan 3, 2023
Dex manipulation library

dexterity Description dexterity is a C library intended for manipulation and analysis of DEX files. It has python bindings for all basic DEX structure

Rodrigo Chiossi 93 Nov 25, 2022
A library for image manipulation with power of renderScript which is faster than other ordinary solutions.

Pixl is a library for image manipulation with power of renderScript which is faster than other ordinary solutions, currently it includes three basic scripts, brightness, contrast, saturation.

Jibran Iqbal 20 Jan 23, 2022
Android & Pure Java library for sound manipulation

soundtransform Android & Pure Java library to shape a voice with an instrument. Table of Contents How to use the library FluentClient FluentClient sam

LiBe 42 Sep 12, 2021
This SDK can be used to identify a user via passport or ID Card prove identity of user via Biometry comparing selfie and photo from chip of ID Doc

Verdi Mobile SDK This SDK can be used to identify a user via passport or Id Card. In this repository, you can find the library itself and the Sample a

null 5 Jan 3, 2022