All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.http4k.util.JsonSchema.kt Maven / Gradle / Ivy

There is a newer version: 5.31.0.0
Show newest version
package org.http4k.util

import org.http4k.format.Json
import org.http4k.format.JsonType
import org.http4k.lens.ParamMeta
import org.http4k.lens.ParamMeta.*

class IllegalSchemaException(message: String) : Exception(message)

data class JsonSchema(val node: NODE, val definitions: Set>)

class JsonToJsonSchema(private val json: Json) {
    fun toSchema(node: NODE, overrideDefinitionId: String? = null) = JsonSchema(node, emptySet()).toSchema(overrideDefinitionId)

    private fun JsonSchema.toSchema(overrideDefinitionId: String? = null): JsonSchema =
        when (json.typeOf(node)) {
            JsonType.Object -> objectSchema(overrideDefinitionId)
            JsonType.Array -> arraySchema()
            JsonType.String -> JsonSchema(StringParam.schema(json.string(json.text(node))), definitions)
            JsonType.Number -> numberSchema()
            JsonType.Boolean -> JsonSchema(BooleanParam.schema(json.boolean(json.bool(node))), definitions)
            JsonType.Null -> throw IllegalSchemaException("Cannot use a null value in a schema!")
            else -> throw IllegalSchemaException("unknown type")
        }

    private fun JsonSchema.numberSchema(): JsonSchema {
        val text = json.text(node)
        val schema = if (text.contains(".")) NumberParam.schema(json.number(text.toBigDecimal())) else IntegerParam.schema(json.number(text.toBigInteger()))
        return JsonSchema(schema, definitions)
    }

    private fun JsonSchema.arraySchema(): JsonSchema {
        val (node, definitions) = json.elements(node).toList().firstOrNull()?.let {
            JsonSchema(it, definitions).toSchema()
        } ?: throw IllegalSchemaException("Cannot use an empty list to generate a schema!")
        return JsonSchema(json { obj("type" to string("array"), "items" to node) }, definitions)
    }

    private fun JsonSchema.objectSchema(overrideDefinitionId: String?): JsonSchema {
        val (fields, subDefinitions) = json.fields(node).fold(listOf>() to definitions) { (memoFields, memoDefinitions), (first, second) ->
            JsonSchema(second, memoDefinitions).toSchema().let { memoFields.plus(first to it.node) to it.definitions }
        }

        val newDefinition = json { obj("type" to string("object"), "properties" to obj(fields)) }
        val definitionId = overrideDefinitionId ?: "object"+newDefinition!!.hashCode()
        val allDefinitions = subDefinitions.plus(definitionId to newDefinition)
        return JsonSchema(json { obj("\$ref" to string("#/definitions/$definitionId")) }, allDefinitions)
    }

    private fun ParamMeta.schema(example: NODE): NODE = json { obj("type" to string(value), "example" to example) }
}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy