/*
* Copyright (c) Huawei Technologies Co., Ltd. 2024-2025. All rights reserved.
*/
package magic.jsonable
import encoding.json.*
import std.collection.{ArrayList, HashMap, map, collectArray}
import encoding.json.JsonNull
public interface ToJsonValue {
func toJsonValue(): JsonValue
}
public interface Jsonable<T> <: ToJsonValue /* where T <: Jsonable<T> */ {
/**
* Get the type schema of T
*/
static func getTypeSchema(): TypeSchema
/**
* Deserialize from a Json value
*/
static func fromJsonValue(json: JsonValue): T
/**
* Deserialize from a Json string
* Since there is a bug in cjc 0.53.x, we cannot add this method.
* Fix it later.
*/
// static func fromJsonStr(jsonStr: String): T {
// return T.fromJsonValue(JsonValue.fromStr(jsonStr))
// }
}
public class JsonableException <: Exception {
public init(msg: String) {
super(msg)
}
}
extend String <: Jsonable<String> {
public static func getTypeSchema(): TypeSchema {
return TypeSchema.Str
}
public static func fromJsonValue(json: JsonValue): String {
JsonUtils.toString(json).getOrThrow({
=> JsonableException("Convert to String error. Value: ${json.toJsonString()}")
})
}
public func toJsonValue(): JsonValue {
return JsonString(this)
}
}
extend Int64 <: Jsonable<Int64> {
public static func getTypeSchema(): TypeSchema {
return TypeSchema.Int
}
public static func fromJsonValue(json: JsonValue): Int64 {
JsonUtils.toInt(json).getOrThrow({ =>
JsonableException("Convert to Int error. Value: ${json.toJsonString()}")
})
}
public func toJsonValue(): JsonValue {
return JsonInt(this)
}
}
extend Float64 <: Jsonable<Float64> {
public static func getTypeSchema(): TypeSchema {
return TypeSchema.Float
}
public static func fromJsonValue(json: JsonValue): Float64 {
JsonUtils.toFloat(json).getOrThrow({ =>
JsonableException("Convert to Float error. Value: ${json.toJsonString()}")
})
}
public func toJsonValue(): JsonValue {
return JsonFloat(this)
}
}
extend Bool <: Jsonable<Bool> {
public static func getTypeSchema(): TypeSchema {
return TypeSchema.Boolean
}
public static func fromJsonValue(json: JsonValue): Bool {
JsonUtils.toBool(json).getOrThrow({
=> JsonableException("Convert to Bool error. Value: ${json.toJsonString()}")
})
}
public func toJsonValue(): JsonValue {
return JsonBool(this)
}
}
extend<T> Array<T> <: Jsonable<Array<T>> where T <: Jsonable<T> {
public static func getTypeSchema(): TypeSchema {
return TypeSchema.Arr(T.getTypeSchema())
}
public static func fromJsonValue(json: JsonValue): Array<T> {
let jsArr = JsonUtils.asJsonArray(json).getOrThrow({
=> JsonableException("Convert to Array error. Value: ${json.toJsonString()}")
})
return jsArr.getItems() |>
map { item: JsonValue => T.fromJsonValue(item) } |>
collectArray
}
public func toJsonValue(): JsonValue {
return JsonArray(
this |> map<T, JsonValue>({ value: T => value.toJsonValue() }) |> collectArray
)
}
}
extend<T> ArrayList<T> <: Jsonable<ArrayList<T>> where T <: Jsonable<T> {
public static func getTypeSchema(): TypeSchema {
return TypeSchema.Arr(T.getTypeSchema())
}
public static func fromJsonValue(json: JsonValue): ArrayList<T> {
let jsArr = JsonUtils.asJsonArray(json).getOrThrow({
=> JsonableException("Convert to Array error. Value: ${json.toJsonString()}")
})
let arr = jsArr.getItems() |>
map { item: JsonValue => T.fromJsonValue(item) } |>
collectArray
return ArrayList<T>(arr)
}
public func toJsonValue(): JsonValue {
return JsonArray(
this.iterator() |>
map { value: T => value.toJsonValue() } |>
collectArray
)
}
}
extend<T> Option<T> <: Jsonable<Option<T>> where T <: Jsonable<T> {
public static func getTypeSchema(): TypeSchema {
// Option<T> has the same type schema as T
// However, Option<T> is not required
return T.getTypeSchema()
}
public static func fromJsonValue(json: JsonValue): Option<T> {
if (let JsonKind.JsNull <- json.kind()) {
return None
}
return Some(T.fromJsonValue(json))
}
public func toJsonValue(): JsonValue {
if (let Some(value) <- this) {
return value.toJsonValue()
} else {
return JsonNull()
}
}
}
extend<T> HashMap<String, T> <: Jsonable<HashMap<String, T>> where T <: Jsonable<T> {
public static func getTypeSchema(): TypeSchema {
// A hash map can be de/serialize to a JSON object;
// however, it has no type schemas because its fields are non-determined.
throw JsonableException("HashMap has no type schema")
}
public static func fromJsonValue(json: JsonValue): HashMap<String, T> {
let map = HashMap<String, T>()
let jsObj = JsonUtils.asJsonObject(json).getOrThrow({
=> JsonableException("Convert to HashMap error. Value: ${json.toJsonString()}")
})
for ((key, value) in jsObj.getFields()) {
map.put(key, T.fromJsonValue(value))
}
return map
}
public func toJsonValue(): JsonValue {
let map = HashMap<String, JsonValue>()
for ((key, value) in this) {
map.put(key, value.toJsonValue())
}
return JsonObject(map)
}
}
extend JsonValue <: ToJsonValue {
public func toJsonValue(): JsonValue {
return this
}
}
extend JsonObject <: Jsonable<JsonObject> {
public static func getTypeSchema(): TypeSchema {
throw JsonableException("Unsupported method")
}
public static func fromJsonValue(json: JsonValue): JsonObject {
return json.asObject()
}
}