Files
serialization-cj/src/json/ReflectionCache.cj
T

124 lines
3.8 KiB
Plaintext
Raw Normal View History

/*
* Copyright (c) 2025 SimcuTeam. All rights reserved.
* ReflectionCache:字段元数据(含继承链、命名策略、@JsonPropertyName/@JsonIgnore)与缓存。
*
* 已知限制(平台级):
* - InstanceVariableInfo.getValue/setValue 有「声明类严格类型校验」,父类字段无法用子类实例读写,
* 故 v1 仅枚举运行时类自身声明的 var 字段(父类字段暂不序列化)。
*/
package simapi_serialization.json
import std.collection.*
import std.reflect.*
/**
* 单个字段的序列化元数据。
*/
public class FieldMetadata {
/// 反射字段名(如 _name
public var name: String = ""
/// JSON 名称(@JsonPropertyName 优先,否则按命名策略)
public var jsonName: String = ""
/// 是否忽略(@JsonIgnore
public var ignore: Bool = false
/// 字段类型
public var typeInfo: ?TypeInfo = None
/// 字段读写句柄
public var variable: ?InstanceVariableInfo = None
/// 是否可变(反序列化只写 var)
public var mutable: Bool = false
public init() {}
}
/**
* 字段反射缓存:类 → 字段列表(首次反射,后续复用)。
*/
public class ReflectionCache {
private init() {}
private static let _cache = HashMap<String, ArrayList<FieldMetadata>>()
/**
* 获取类型的字段元数据(含缓存)。
* @param typeInfo 目标类类型。
* @param options 序列化选项(命名策略)。
* @return 字段列表(仅该类自身声明的 var 字段)。
*/
public static func getFields(typeInfo: ClassTypeInfo, options: JsonOption): ArrayList<FieldMetadata> {
let key = typeInfo.qualifiedName
if (let Some(cached) <- _cache.get(key)) {
return cached
}
var fields = ArrayList<FieldMetadata>()
for (v in typeInfo.instanceVariables) {
var meta = FieldMetadata()
meta.name = v.name
meta.variable = Some(v)
meta.typeInfo = Some(v.typeInfo)
meta.mutable = v.isMutable()
// @JsonIgnore
if (v.findAnnotation<JsonIgnore>().isSome()) {
meta.ignore = true
}
// @JsonPropertyName 优先
var jsonName = ""
if (let Some(jsonProp) <- v.findAnnotation<JsonPropertyName>()) {
jsonName = jsonProp.name
} else {
jsonName = applyNamingPolicy(v.name, options.propertyNamingPolicy)
}
meta.jsonName = jsonName
fields.add(meta)
}
_cache[key] = fields
fields
}
/**
* 应用属性命名策略。
*/
public static func applyNamingPolicy(fieldName: String, policy: PropertyNamingPolicy): String {
var name = fieldName
while (name.startsWith("_")) {
name = name[1..]
}
match (policy) {
case PropertyNamingPolicy.None => name
case PropertyNamingPolicy.CamelCase => toCamelCase(name)
case PropertyNamingPolicy.SnakeCase => toSnakeCase(name)
}
}
private static func toCamelCase(s: String): String {
if (s.isEmpty()) {
return s
}
let first = s[0..1].toAsciiLower()
"${first}${s[1..]}"
}
private static func toSnakeCase(s: String): String {
if (s.isEmpty()) {
return s
}
var sb = StringBuilder()
var first = true
for (c in s.runes()) {
let ch = UInt32(c)
let isUpper = ch >= 0x41 && ch <= 0x5A
if (isUpper) {
if (!first) {
sb.append("_")
}
sb.append(Rune(ch + 0x20))
} else {
sb.append(c)
}
first = false
}
sb.toString()
}
}