Загрузка данных
package ru.notesapp.util
import kotlin.reflect.KClass
import kotlin.reflect.KParameter
import kotlin.reflect.full.declaredMemberProperties
import kotlin.reflect.full.primaryConstructor
// ---------- toJson ----------
fun Any.toJson(): String {
val klass = this::class
if (klass.annotations.none { it is MiniSerializable }) {
throw IllegalArgumentException(
"Class ${klass.simpleName} is not annotated with @MiniSerializable"
)
}
val sb = StringBuilder("{")
var first = true
for (prop in klass.declaredMemberProperties) {
val value = prop.getter.call(this)
val ann = prop.annotations.filterIsInstance<MiniSerializable>().firstOrNull()
val key = ann?.name?.takeIf { it.isNotEmpty() } ?: prop.name
if (!first) sb.append(",")
first = false
sb.append("\"").append(key).append("\":").append(valueToJson(value))
}
sb.append("}")
return sb.toString()
}
private fun valueToJson(value: Any?): String = when (value) {
null -> "null"
is String -> "\"${escapeJson(value)}\""
is Number, is Boolean -> value.toString()
is Enum<*> -> "\"${value.name}\""
else -> value.toJson()
}
private fun escapeJson(s: String): String {
val sb = StringBuilder()
for (c in s) {
when (c) {
'\\' -> sb.append("\\\\")
'"' -> sb.append("\\\"")
'\n' -> sb.append("\\n")
'\r' -> sb.append("\\r")
'\t' -> sb.append("\\t")
else -> sb.append(c)
}
}
return sb.toString()
}
// ---------- fromJson ----------
fun <T : Any> fromJson(json: String, klass: KClass<T>): T {
if (klass.annotations.none { it is MiniSerializable }) {
throw IllegalArgumentException(
"Class ${klass.simpleName} is not annotated with @MiniSerializable"
)
}
val ctor = klass.primaryConstructor
?: throw IllegalArgumentException("No primary constructor for ${klass.simpleName}")
val map = parseSimpleJson(json)
val propsByName = klass.declaredMemberProperties.associateBy { it.name }
val args = mutableMapOf<KParameter, Any?>()
for (param in ctor.parameters) {
val prop = propsByName[param.name]
val ann = prop?.annotations?.filterIsInstance<MiniSerializable>()?.firstOrNull()
?: param.annotations.filterIsInstance<MiniSerializable>().firstOrNull()
val key = ann?.name?.takeIf { it.isNotEmpty() } ?: param.name
if (map.containsKey(key)) {
args[param] = convertValue(map[key], param)
} else if (!param.isOptional) {
throw IllegalArgumentException("Missing required parameter '$key'")
}
}
return ctor.callBy(args)
}
@Suppress("UNCHECKED_CAST")
private fun convertValue(value: Any?, param: KParameter): Any? {
return when (param.type.classifier) {
String::class -> value?.toString()
Long::class -> when (value) {
null -> 0L
is Long -> value
is Number -> value.toLong()
else -> value.toString().toLong()
}
Int::class -> when (value) {
null -> 0
is Int -> value
is Number -> value.toInt()
else -> value.toString().toInt()
}
Double::class -> when (value) {
null -> 0.0
is Double -> value
is Number -> value.toDouble()
else -> value.toString().toDouble()
}
Boolean::class -> when (value) {
null -> false
is Boolean -> value
else -> value.toString().toBoolean()
}
else -> value
}
}
// ---------- простейший парсер JSON ----------
private fun parseSimpleJson(json: String): Map<String, Any?> {
val s = json.trim()
if (!s.startsWith("{") || !s.endsWith("}")) {
throw IllegalArgumentException("JSON must be an object")
}
val inner = s.substring(1, s.length - 1)
val result = mutableMapOf<String, Any?>()
var i = 0
while (i < inner.length) {
while (i < inner.length && (inner[i].isWhitespace() || inner[i] == ',')) i++
if (i >= inner.length) break
if (inner[i] != '"') throw IllegalArgumentException("Expected '\"' at $i")
i++
val keySb = StringBuilder()
while (i < inner.length && inner[i] != '"') {
if (inner[i] == '\\' && i + 1 < inner.length) {
i++
keySb.append(unescape(inner[i]))
} else {
keySb.append(inner[i])
}
i++
}
if (i >= inner.length) throw IllegalArgumentException("Unterminated key")
i++
val key = keySb.toString()
while (i < inner.length && inner[i].isWhitespace()) i++
if (i >= inner.length || inner[i] != ':') throw IllegalArgumentException("Expected ':' at $i")
i++
while (i < inner.length && inner[i].isWhitespace()) i++
if (i >= inner.length) throw IllegalArgumentException("Missing value")
val value: Any? = when (inner[i]) {
'"' -> {
i++
val sb = StringBuilder()
while (i < inner.length && inner[i] != '"') {
if (inner[i] == '\\' && i + 1 < inner.length) {
i++
sb.append(unescape(inner[i]))
} else {
sb.append(inner[i])
}
i++
}
if (i >= inner.length) throw IllegalArgumentException("Unterminated string")
i++
sb.toString()
}
'n' -> {
if (!inner.startsWith("null", i)) throw IllegalArgumentException("Invalid value at $i")
i += 4
null
}
't' -> {
if (!inner.startsWith("true", i)) throw IllegalArgumentException("Invalid value at $i")
i += 4
true
}
'f' -> {
if (!inner.startsWith("false", i)) throw IllegalArgumentException("Invalid value at $i")
i += 5
false
}
else -> {
val start = i
while (i < inner.length && inner[i] != ',' && inner[i] != '}') i++
val numStr = inner.substring(start, i).trim()
if (numStr.contains('.') || numStr.contains('e') || numStr.contains('E')) {
numStr.toDouble()
} else {
numStr.toLong()
}
}
}
result[key] = value
}
return result
}
private fun unescape(c: Char): Char = when (c) {
'n' -> '\n'
't' -> '\t'
'r' -> '\r'
'"' -> '"'
'\\' -> '\\'
'/' -> '/'
else -> c
}