Gson TypeAdapter & Factory generator for Kotlin data classes

Overview

maven Android Arsenal

Kson - kotlin type adapter generator

An annotation processor generates Gson TypeAdapter from Kotlin Data Classes

Motivation

By default, Gson uses reflection to read/write data from JSON. It's not only slow (benchmarks), also it breaks Kotlin's null-safe types.

For example:

// your entity class with non-nullable property
data class Entity(val id: Int)

// wrong response from server
val json = """{ "id": null }"""

// this won't throw error
val entity = gson.fromJson(json, Entity::class.java)

// throws NPE somewhere in runtime when you don't expect
entity.id

In order to avoid reflection, need to create a custom TypeAdapter for every entity class. It takes time, need to write boilerplate code, tests, etc. Here this library comes, it generates TypeAdapters automatically. You just need to register generated GsonTypeAdapterFactory in your GsonBuilder.

Usage

Add @Kson annotation to your data classes and Kson will automatically generate <class name>TypeAdapter.kt files.

@Kson
data class RoleEntity(
    val id: Int, 
    @SerializedName("roleName") val name: String
)

@Kson
data class UserEntity(
    val firstname: String,
    val lastname: String,
    val roles: List<RoleEntity>
)

// etc

Also you can use @KsonFactory annotation to generate TypeAdapterFactory class

@KsonFactory
object FactoryProvider {

    get() = KsonFactoryProvider()

}

val gson = GsonBuilder()
    .registerTypeAdapterFactory(FactoryProvider.get())
    .create()

// gson.fromJson(...)

Limitations & Known issues

Since this is an early version there are some unsupported properties

@Kson
data class UnsupportedDataClass(
    @JsonAdapter(CustomAdapter::class) val id: String // custom type adapter
    val name: String = "no name" // default values
    val list: List<String?> // nullable generics
    val `natural name`: String // "natural names"
)

Installation

To add KSON to your project, add the following to your module's build.gradle:

repositories {
    jcenter()
}

dependencies {
    compile 'dev.afanasev:kson-annotation:<version>'   
    kapt 'dev.afanasev:kson-processor:<version>'
}

Mentions

Code of Conduct

Please refer to Code of Conduct document.

Comments
  • added filter static fields

    added filter static fields

    Hello. When I am trying to generate TypeAdapter for class with companion object and some constants in this object I have an error because TypeAdapter tries to write and read static field and companion field from JsonObject

    opened by Mefi100feLL 0
  • Support incremental compilation.

    Support incremental compilation.

    Support for https://github.com/aafanasev/kson/issues/5

    So, what's going on is:

    1. To support incremental compilation all files created by annotation processor have to be created using Filer. To do so I have to update KotlinPoet to latest version (1.3.0) which led to some methods being removed, moved or changed visibility. Thus, I created file KotlinPoetExtensions.kt with some extension methods to match old api.
    2. Another thing is we should provide originating element to all files, so I have to change some methods to work with TypeElement instead of ClassName.
    3. I have to update Kotlin to version 1.3.40 because of this bug.
    4. For better performance I split processor into two: one for generating TypeAdapter (this one is ISOLATING) and one for generating TypeAdapterFactory (this one is AGGREGATING).
    5. I also use AutoService to register annotation processors which is more convenient imo.
    opened by bejibx 0
  • Generate adapters based on type, not property

    Generate adapters based on type, not property

    @Kson
    data class UserEntity(
        val firstname: String, 
        val lastname: String
    )
    
    // generated code
    ...
    private val firstnameAdapter by lazy { gson.getAdapter(String::class.java) }  
    private val lastnameAdapter by lazy { gson.getAdapter(String::class.java) }  
    ...
    

    As you see, each property will have it's own adapter. I think better to have adapters grouped by types, f.ex:

    private val stringAdapter by lazy { gson.getAdapter(String::class.java) } 
    

    and use this adapter in both properties

    optimization 
    opened by aafanasev 0
  • Don't use synchronized delegates

    Don't use synchronized delegates

    Now generated code looks like this:

    private val adapter by lazy { gson.getAdapter(...) }

    Since the evaluation of the delegated properties is synchronized and it's redundant in TypeAdapter case, need to use LazyThreadSafetyMode.NONE mode, which doesn't incur any thread-safety guarantees and the related overhead.

    help wanted optimization 
    opened by aafanasev 0
  • Support gson RuntimeTypeAdapterFactory generation

    Support gson RuntimeTypeAdapterFactory generation

    gson-extras has such class: https://github.com/google/gson/blob/master/extras/src/main/java/com/google/gson/typeadapters/RuntimeTypeAdapterFactory.java which is helpful for polymorphic types serialization without reflection.

    For example, it can be used to handle Kotlin sealed class serialization:

    sealed class Shape {
            class Rectangle() : Shape()
            class Circle() : Shape()
    }
    
    val adapter: RuntimeTypeAdapterFactory<Shape> = RuntimeTypeAdapterFactory.of(Shape::class.java)
            .registerSubtype(Rectangle::class.java)
            .registerSubtype(Circle::class.java)
    

    And then in your gson instance builder:

    registerTypeAdapterFactory(adapter)

    However, seems it is a boilerplate and not safe solution to write such factory for every sealed class in the project. Would be nice to handle it with annotations like kotlinx-serialization does with polymorphic serialization problems: https://medium.com/@andreclassen1337/goodbye-runtimetypeadapterfactory-polymorphic-serialization-using-kotlinx-serialization-46a8cec36fdc

    @Polymorphic
    sealed class Shape {
            @SerialName("Rectangle")
            class Rectangle() : Shape()
            @SerialName("Circle")
            class Circle() : Shape()
    }
    
    opened by GediminasZukas 2
Releases(1.1)
Owner
Anatolii Afanasev
Software Engineer
Anatolii Afanasev
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
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
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
Tired of manually setup test data of Kotlin data classes or POJOs? Instantiator creates Instances of any class for you so that you can focus on writing tests instead of spending time and effort to setup test data

Instantiator Tired of manually setup test data of Kotlin data classes or POJOs? Instantiator creates Instances of any class for you so that you can fo

Hannes Dorfmann 54 Dec 30, 2022
Mirai-device-generator - Mirai Device Generator with kotlin

Mirai Device Generator Mirai DeviceInfo 生成器 作为插件运行时会提供 BotConfigurationAlterer 服

cssxsh 46 Jan 1, 2023
Usages of Factory Method for Data Source Layer (Local/Remote - Repository) with DI & MVVM [Android].

Usages of Factory Method for Data Source Layer (Local/Remote - Repository) with DI & MVVM [Android] Stacks: MVVVM DI (Hilt) Factory Method (Design Pat

Romman Sabbir 4 Aug 9, 2022
LiveDataCallAdapter - Live Data Call Adapter Factory

LiveDataCallAdapterFactory based on retrofit, the LiveData returned by the restf

Jay Wang 0 Feb 6, 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
Fixture factory in Kotlin

KFactory Create best-in-class factories for your synthetic data in Kotlin. ⭐ Test fixtures ⭐ DB seeding ⭐ Feature demos ⭐ Pre-production environments

Blueground 51 Dec 20, 2022
AbstractFactoryDesignPatternWithKotlin - Abstract Factory Design Pattern With Kotlin

AbstractFactoryDesignPatternWithKotlin Abstract Factory Design Pattern With Kotl

Harun Kör 2 Jun 16, 2022
AbstractFactoryDesignPatternWithKotlin - Abstract Factory Design Pattern With Kotlin

AbstractFactoryDesignPatternWithKotlin Abstract Factory Design Pattern With Kotl

Harun Kör 1 Jan 2, 2022
Adding support for Factory boy and django packages

This plugin provides some support for Factory Boy Features autocomplete for instances created by factories references to members of instance class Not

Nazareka 3 Dec 19, 2021
Retrofit Flow Call Adapter Factory For Android

Summary Android Retrofit FlowCallAdapterFactory Retrofit 2 CallAdapter.Factory f

TaeHwan 12 Aug 3, 2022
Small kotlin library for persisting _single instances_ of kotlin data classes

PerSista Small library for persisting single instances of kotlin data classes. NB: PerSista uses typeOf() internally which is marked as @ExperimentalS

Eric Donovan 5 Nov 13, 2022
The Kotlin fake data generator library!

Fakeit This library is a port of the Ruby gem Faker. It generates realistic fake data — like names, emails, dates, countries — for a variety of scenar

Moove It 517 Nov 20, 2022
An AutoValue extension that generates binary and source compatible equivalent Kotlin data classes of AutoValue models.

AutoValue Kotlin auto-value-kotlin (AVK) is an AutoValue extension that generates binary-and-source-compatible, equivalent Kotlin data classes. This i

Slack 19 Aug 5, 2022
A Secure Password Generator designed with security precautions for the user's data

GenPass GenPass is a secure password generating application designed with a high level of security. It uses Java Security library to generate strong p

Joseph Olugbohunmi 1 Apr 29, 2022