org.http4k.format.Gson.kt Maven / Gradle / Ivy
package org.http4k.format
import com.google.gson.GsonBuilder
import com.google.gson.JsonArray
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 org.http4k.core.Body
import org.http4k.lens.BiDiBodyLensSpec
import org.http4k.lens.BiDiWsMessageLensSpec
import org.http4k.lens.ContentNegotiation
import org.http4k.websocket.WsMessage
import java.math.BigDecimal
import java.math.BigInteger
import kotlin.reflect.KClass
class InvalidJsonException(messasge: String, cause: Throwable? = null) : Exception(messasge, cause)
open class ConfigurableGson(builder: GsonBuilder) : JsonLibAutoMarshallingJson() {
override fun typeOf(value: JsonElement): JsonType =
when {
value.isJsonArray -> JsonType.Array
value.isJsonNull -> JsonType.Null
value.isJsonObject -> JsonType.Object
value.isJsonPrimitive -> {
val prim = value.asJsonPrimitive
when {
prim.isBoolean -> JsonType.Boolean
prim.isNumber -> JsonType.Number
prim.isString -> JsonType.String
else -> throw IllegalArgumentException("Don't know now to translate $value")
}
}
else -> throw IllegalArgumentException("Don't know now to translate $value")
}
private val compact = builder.create()
private val pretty = builder.setPrettyPrinting().create()
override fun String.asJsonObject(): JsonElement = JsonParser().parse(this).let { if(it.isJsonArray || it.isJsonObject) it else throw InvalidJsonException(
"Could not convert to a JSON Object or Array. $this"
) }
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 = compact.toJson(this)
override fun >> LIST.asJsonObject(): JsonElement {
val root = JsonObject()
forEach { root.add(it.first, it.second) }
return root
}
override fun fields(node: JsonElement): Iterable> {
val fieldList = mutableListOf>()
for ((key, value) in node.asJsonObject.entrySet()) {
fieldList += key to value
}
return fieldList
}
override fun elements(value: JsonElement): Iterable = value.asJsonArray
override fun text(value: JsonElement): String = value.asString
override fun asJsonObject(a: Any): JsonElement = compact.toJsonTree(a)
override fun asA(s: String, c: KClass): T = compact.fromJson(s, c.java)
override fun asA(j: JsonElement, c: KClass): T = compact.fromJson(j, c.java)
inline fun String.asA(): T = asA(this, T::class)
inline fun JsonElement.asA(): T = asA(this, T::class)
inline fun Body.Companion.auto(description: String? = null, contentNegotiation: ContentNegotiation = ContentNegotiation.None): BiDiBodyLensSpec = Body.json(description, contentNegotiation).map({ it.asA() }, { it.asJsonObject() })
inline fun WsMessage.Companion.auto(): BiDiWsMessageLensSpec = WsMessage.json().map({ it.asA() }, { it.asJsonObject() })
}
object Gson : ConfigurableGson(GsonBuilder().serializeNulls())