Skip to content

Instantly share code, notes, and snippets.

@vincentdaniel
Created January 24, 2018 16:34
Show Gist options
  • Select an option

  • Save vincentdaniel/cb630b03c39084f9ac7a367f922cf342 to your computer and use it in GitHub Desktop.

Select an option

Save vincentdaniel/cb630b03c39084f9ac7a367f922cf342 to your computer and use it in GitHub Desktop.

Revisions

  1. Vincent Daniel created this gist Jan 24, 2018.
    80 changes: 80 additions & 0 deletions jsonToDataClass.kt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,80 @@
    import com.fasterxml.jackson.core.type.TypeReference
    import com.fasterxml.jackson.databind.ObjectMapper
    import java.io.File


    object Main {

    /**
    * - Reads a file that contains multiple json messages:
    * ```
    * {"id":12345,"some_key1":"value1"}
    * {"id":12346,"some_key1":"value2","some_optional_key2":"value3"}
    * ...
    * ```
    * - Generates the corresponding data class:
    * ```
    * data class MyClass(
    * val id: Int? = null,
    * val someKey1: String? = null,
    * val someOptionalKey2: String? = null
    * )
    * ```
    */
    @JvmStatic
    fun main(args: Array<String>) {
    val jsonLines = readFile("sample-data.log")
    println(jsonLines.toDataClass("MyClass"))
    }

    private fun readFile(file: String): List<String> {
    val inputStream = File(file).inputStream()
    val lineList = mutableListOf<String>()
    inputStream.bufferedReader().useLines { lines -> lines.forEach { lineList.add(it)} }
    return lineList
    }

    private fun List<String>.toDataClass(name: String): String = this.extractSchema().toDataClass(name)

    private fun List<String>.extractSchema(): Map<String, String> {
    val objectMapper = ObjectMapper()
    val schema = mutableMapOf<String, String>()
    this.forEach { schema.updateWith(it.parseSchema(objectMapper)) }
    return schema
    }

    private fun MutableMap<String, String>.updateWith(other: Map<String, String>) {
    other.forEach { key, value ->
    this.compute(key, { _, oldValue ->
    when (oldValue) {
    null -> value
    "null" -> value
    else -> oldValue
    }
    })
    }
    }

    private fun Map<String, String>.toDataClass(name: String): String {
    val lineSeparator = System.getProperty("line.separator") ?: "\n"
    val types = this.map { (key, javaType) ->
    val kotlinType = when (javaType) {
    "class java.lang.Integer" -> "Int"
    "class java.lang.Boolean" -> "Boolean"
    else -> "String"
    }
    "val $key: $kotlinType? = null"
    }.joinToString(",$lineSeparator")

    return "data class $name($types)"
    }

    private fun String.parseSchema(objectMapper: ObjectMapper): Map<String, String> {
    val jsonMap: Map<String, Any?> = objectMapper.readValue(this, object : TypeReference<Map<String, Any?>>() {})
    return jsonMap.map { (key, value) ->
    Pair(key.toCamelCase(), value?.javaClass.toString())
    }.toMap().toSortedMap()
    }

    private fun String.toCamelCase(): String = this.split('_').joinToString("") { it.capitalize() }.decapitalize()
    }