Skip to content

uharaqo/kotlin-hocon-mapper

Repository files navigation

kotlin-hocon-mapper

Maven Central Build Status codecov Sonarcloud Status license

Overview

A lightweight Typesafe Config (HOCON) mapper for Kotlin classes based on kotlinx.serialization.

  • Provides serializers and deserializers based on kotlinx.serialization
    • Deserializers convert com.typesafe.config.Config into a class annotated with @Serializable
    • Serializers convert a serializable object into a JSON (Beta: intended to support writing HOCON config during development)
    • No reflection at runtime. Those serializers and deserializers are generated at compile time.
  • Supports basic types such as String, Boolean, Int, Long, Float, Double, Enum, List, Map and nested object
  • Supports additional types for unit conversion such as Period, Duration and ConfigMemorySize

Core Dependencies

  • version 1.4.10:

    • kotlin: 1.4.10
    • kotlinx.serialization: 1.0.0-RC
    • typesafe.config: 1.4.0
  • version 0.9.0:

    • kotlin: 1.3.72
    • kotlinx.serialization: 0.20.0
    • typesafe.config: 1.4.0
  • version 0.1.0:

    • kotlin: 1.3.50
    • kotlinx.serialization: 0.13.0
    • typesafe.config: 1.3.4

Getting Started

  • Gradle

    implementation "com.github.uharaqo.kotlin-hocon-mapper:kotlin-hocon-mapper:$hocon_mapper_version"
  • Maven

    <dependency>
      <groupId>com.github.uharaqo.kotlin-hocon-mapper</groupId>
      <artifactId>kotlin-hocon-mapper</artifactId>
      <version>${hocon.mapper.version}</version>
    </dependency>
  • Setup the kotlinx.serializer compiler plugin

  • To convert a Config object into a data object,

    @Serializable
    data class BasicTypes(
        val char: Char, val string: String, val bool: Boolean,
        val byte: Byte, val int: Int, val long: Long,
        val short: Short, val float: Float, val double: Double,
        val enum: BasicModels.SampleEnum, val nullable: String?,
        val list: List<Int>, val map: Map<String, Int>,
        val nested: Nested
    )
    @Serializable
    data class Nested(val value: String)
    
    // Config and ConfigFactory are found in com.typesafe.config package
    val config: Config = ConfigFactory.parseString(
        """{
         |  char: a,
         |  string: abc,
         |  bool: true,
         |  byte: 1,
         |  int: ${Int.MAX_VALUE},
         |  long: ${Long.MAX_VALUE},
         |  short: ${Short.MAX_VALUE},
         |  float: ${Float.MAX_VALUE},
         |  double: ${Double.MAX_VALUE},
         |  enum: ${BasicModels.SampleEnum.ELEMENT}
         |  nullable: null,
         |  list: [1, 2, 3]
         |  map: { first: 1, second: 2}
         |  nested: { value: nested }
         |  unknown: "this value is ignored becuase 'unknown' is not defined in the data object"
         |}
         """.trimMargin()
    )
    val obj = BasicTypes.serializer().load(config)
  • To convert additional types for unit conversion,

    // file targeted annotation to enable these deserializers
    @file:UseSerializers(
        DurationSerializer::class,
        PeriodSerializer::class,
        ConfigMemorySizeSerializer::class
    )
    
    @Serializable
    data class UnitConversion(
      val duration: Duration,        // -> Duration.ofMinutes(10)
      val period: Period,            // -> Period.ofWeeks(1)
      val memSize: ConfigMemorySize, // -> ConfigMemorySize.ofBytes(1024)
      // this is another option to enable an additional serializer for a field
      @Serializable(with = StringBooleanSerializer::class)
      val textBoolean: Boolean       // -> true
    )
    
    val config: com.typesafe.config.Config = ConfigFactory.parseString(
        """{
         |  duration: 10m
         |  period: 1w
         |  memSize: 1KiB
         |  textBoolean: yes
         |}
         """.trimMargin()
    )
    val converted = UnitConversion.serializer().load(conf)

    More examples can be found in test code

  • To serialize an object into a JSON String (Beta)

    val json: String = BasicTypes.serializer().stringify(data)
    // or ConfigSerializer.stringify(BasicTypes.serializer(), data)

Deserialization details

All the arguments in a constructor should be provided by a Config object

  • When a key is not found in the Config, MissingFieldException will be thrown
  • null can be injected into a nullable argument, but it should be explicitly defined like {"path.to.prop": null})
  • Default values in a constructor are ignored

Example usage

Production code

  1. Create a data class with the @Serializable annotation
  2. Load config files by using the Typesafe Config
  3. Load the Config object into the data class by using the deserializer

Setup the config by temporary code

  1. Instantiate the class with default values
  2. Generate a JSON with the object by using the serializer
  3. Put the JSON into a config source such as application.conf

Links

  • HOCON

    • Human-Optimized Config Object Notation
    • JSON-like intuitive configuration syntax
    • Supports lots of useful features
    • Adopted by popular libraries such as Ktor
  • Typesafe Config

    • A library for managing HOCON files
    • merge configs from multiple source such as files, system properties, environment variables
  • kotlinx.serialization

    • A compiler plugin which generates code to serialize objects without reflection.
    • A runtime library which provides APIs for Encoder and Decoder
  • Config4k

    • Kotlin extension functions for Typesafe Config
    • Object mapper: config.extract<MyData>("path.to.mydata")
    • Serializer: MyData("foo", 42).toConfig("path.to.mydata")
    • Unfortunately, this project has been inactive since Jan 2019 (as of Sep 2019). It didn't work with the latest kotlin reflection library.

About

A lightweight Typesafe Config (HOCON) mapper for Kotlin classes based on kotlinx.serialization

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages