All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.http4k.format.ConfigurableGson.kt Maven / Gradle / Ivy
package org.http4k.format
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonNull
import com.google.gson.JsonObject
import com.google.gson.JsonParser
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import org.http4k.core.Body
import org.http4k.core.ContentType
import org.http4k.core.ContentType.Companion.APPLICATION_JSON
import org.http4k.core.HttpMessage
import org.http4k.core.with
import org.http4k.lens.BiDiMapping
import org.http4k.lens.ContentNegotiation
import org.http4k.lens.ContentNegotiation.Companion.None
import org.http4k.websocket.WsMessage
import java.io.InputStream
import java.lang.reflect.Type
import java.math.BigDecimal
import java.math.BigInteger
import kotlin.reflect.KClass
open class ConfigurableGson(
builder: GsonBuilder,
override val defaultContentType: ContentType = APPLICATION_JSON
) : AutoMarshallingJson() {
val mapper: Gson = builder.create()
private val pretty = builder.setPrettyPrinting().create()
override fun typeOf(value: JsonElement): JsonType =
when {
value.isJsonArray -> JsonType.Array
value.isJsonNull -> JsonType.Null
value.isJsonObject -> JsonType.Object
value.isJsonPrimitive -> with(value.asJsonPrimitive) {
when {
isBoolean -> JsonType.Boolean
isNumber -> if (value.asString.any { !it.isDigit() }) JsonType.Number else JsonType.Integer
isString -> JsonType.String
else -> throw IllegalArgumentException("Don't know how to translate $value")
}
}
else -> throw IllegalArgumentException("Don't know how to translate $value")
}
override fun String.asJsonObject(): JsonElement = JsonParser.parseString(this).let {
if (it.isJsonArray || it.isJsonObject) it else throw InvalidJsonException(
"Could not convert to a JSON Object or Array. $this"
)
}
inline fun R.with(t: T): R = with(Body.auto().toLens() of t)
override fun String?.asJsonValue(): JsonElement = this?.let { JsonPrimitive(this) } ?: JsonNull.INSTANCE
override fun Int?.asJsonValue(): JsonElement = this?.let { JsonPrimitive(this) } ?: JsonNull.INSTANCE
override fun Double?.asJsonValue(): JsonElement = this?.let { JsonPrimitive(this) } ?: JsonNull.INSTANCE
override fun Long?.asJsonValue(): JsonElement = this?.let { JsonPrimitive(this) } ?: JsonNull.INSTANCE
override fun BigDecimal?.asJsonValue(): JsonElement = this?.let { JsonPrimitive(this) } ?: JsonNull.INSTANCE
override fun BigInteger?.asJsonValue(): JsonElement = this?.let { JsonPrimitive(this) } ?: JsonNull.INSTANCE
override fun Boolean?.asJsonValue(): JsonElement = this?.let { JsonPrimitive(this) } ?: JsonNull.INSTANCE
override fun > T.asJsonArray(): JsonElement =
fold(JsonArray()) { memo, o -> memo.add(o); memo }
override fun JsonElement.asPrettyJsonString(): String = pretty.toJson(this)
override fun JsonElement.asCompactJsonString(): String = mapper.toJson(this)
override fun >> LIST.asJsonObject() =
JsonObject().apply { forEach { add(it.first, it.second) } }
override fun fields(node: JsonElement): Iterable> =
if (typeOf(node) != JsonType.Object) emptyList() else {
val fieldList = mutableListOf>()
for ((key, value) in node.asJsonObject.entrySet()) {
fieldList += key to value
}
fieldList
}
override fun elements(value: JsonElement): Iterable = value.asJsonArray
override fun text(value: JsonElement): String = if (value is JsonNull) "null" else value.asString
override fun bool(value: JsonElement): Boolean = value.asBoolean
override fun integer(value: JsonElement) = value.asLong
override fun decimal(value: JsonElement): BigDecimal = value.asBigDecimal
override fun textValueOf(node: JsonElement, name: String): String = when (node) {
is JsonObject -> node[name].asString
else -> throw IllegalArgumentException("node is not an object")
}
/**
* Convenience function to write the object as JSON to the message body and set the content type.
*/
inline fun R.yaml(t: T): R = with(Body.auto().toLens() of t)
/**
* Convenience function to read an object as YAML from the message body.
*/
inline fun HttpMessage.yaml(): T = Body.auto().toLens()(this)
// auto
override fun asJsonObject(input: Any): JsonElement = mapper.toJsonTree(input)
override fun asA(input: String, target: KClass): T = mapper.fromJson(input, target.java)
override fun asA(input: InputStream, target: KClass): T =
mapper.fromJson(JsonReader(input.reader()), target.java)
override fun asA(j: JsonElement, target: KClass): T = mapper.fromJson(j, target.java)
inline fun JsonElement.asA(): T = mapper.fromJson(this, object : TypeToken() {}.type)
inline fun WsMessage.Companion.auto() =
WsMessage.json().map({ it.asA() }, { it.asJsonObject() })
inline fun Body.Companion.auto(
description: String? = null,
contentNegotiation: ContentNegotiation = None,
contentType: ContentType = defaultContentType
) = autoBody(description, contentNegotiation, contentType)
inline fun autoBody(
description: String? = null,
contentNegotiation: ContentNegotiation = None,
contentType: ContentType = defaultContentType
) =
httpBodyLens(description, contentNegotiation, contentType).map(mapper.read()) { mapper.toJson(it) }
}
inline fun Gson.read(): (String) -> T =
{ fromJson(it, object : TypeToken() {}.type) as T }
class InvalidJsonException(message: String, cause: Throwable? = null) : RuntimeException(message, cause)
fun GsonBuilder.asConfigurable() = object : AutoMappingConfiguration {
override fun int(mapping: BiDiMapping) = adapter(mapping, ::JsonPrimitive, JsonElement::getAsInt)
override fun long(mapping: BiDiMapping) = adapter(mapping, ::JsonPrimitive, JsonElement::getAsLong)
override fun double(mapping: BiDiMapping) =
adapter(mapping, ::JsonPrimitive, JsonElement::getAsDouble)
override fun boolean(mapping: BiDiMapping) =
adapter(mapping, ::JsonPrimitive, JsonElement::getAsBoolean)
override fun bigInteger(mapping: BiDiMapping) =
adapter(mapping, ::JsonPrimitive, JsonElement::getAsBigInteger)
override fun bigDecimal(mapping: BiDiMapping) =
adapter(mapping, ::JsonPrimitive, JsonElement::getAsBigDecimal)
override fun text(mapping: BiDiMapping) =
adapter(mapping, ::JsonPrimitive, JsonElement::getAsString)
private fun adapter(
mapping: BiDiMapping,
asPrimitive: IN.() -> JsonPrimitive,
value: JsonElement.() -> IN
) =
apply {
[email protected] (mapping.clazz, object : JsonSerializer, JsonDeserializer {
override fun serialize(src: OUT, typeOfSrc: Type, context: JsonSerializationContext) =
mapping(src).asPrimitive()
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext) =
mapping.invoke(json.value())
})
}
override fun done(): GsonBuilder = this@asConfigurable
}
inline operator fun ConfigurableGson.invoke(msg: HttpMessage): T = autoBody().toLens()(msg)
inline operator fun ConfigurableGson.invoke(item: T) = autoBody().toLens().of(item)