Files
serialization-cj/src/common/ReflectionCache.cj
T
xrain f593f96d9a refactor: 新增 common 子包——XML/JSON 共用部分 + 单 import 全量可用
- src/common/(package simapi_serialization.common):
  Annotations.cj(@SerializerPropertyName/@SerializerIgnore)、
  ReflectionCache.cj(字段元数据 + 继承链缓存)、NamingPolicy.cj(PropertyNamingPolicy)
- 根锚点 public import common.* + json.*:import simapi_serialization.* 即可用全部
- JsonOption.propertyNamingPolicy 引用 common 枚举;getFields 改收命名策略
- 枚举值 PropertyNamingPolicy.None → Keep(None 与内置 Option.None 构造器同名冲突)
- JsonWriter/JsonReader 单类 import common,避免循环依赖
- 测试/独立项目验证:16 用例全绿;serialization_test 单 import 即可运行
2026-08-17 14:06:13 +08:00

160 lines
5.4 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2025 SimcuTeam. All rights reserved.
* ReflectionCache:字段元数据(含继承链、命名策略、@SerializerPropertyName/@SerializerIgnore)与缓存。
*
* 已知限制(平台级):
* - InstanceVariableInfo.getValue/setValue 有「声明类严格类型校验」,父类字段无法用子类实例读写,
* 故 v1 仅枚举运行时类自身声明的 var 字段(父类字段暂不序列化)。
*/
package simapi_serialization.common
import std.collection.*
import std.reflect.*
/**
* 单个字段的序列化元数据。
*/
public class FieldMetadata {
/// 反射字段名(如 _name
public var name: String = ""
/// JSON 名称(@SerializerPropertyName 优先,否则按命名策略)
public var jsonName: String = ""
/// 是否忽略(@SerializerIgnore
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>>()
/// 类型 → 是否有 @SerializerParent 宏生成的 exportJsonFieldsBool
private static let _exportCache = HashMap<String, Bool>()
/// 类型 → 是否有 @SerializerParent 宏生成的 importJsonFieldsBool
private static let _importCache = HashMap<String, Bool>()
/**
* 该类型是否含 @SerializerParent 宏生成的导出静态方法(含缓存)。
*/
public static func hasExportFields(typeInfo: ClassTypeInfo): Bool {
hasParentMethod(typeInfo, "exportJsonFields", _exportCache)
}
/**
* 该类型是否含 @SerializerParent 宏生成的导入静态方法(含缓存)。
*/
public static func hasImportFields(typeInfo: ClassTypeInfo): Bool {
hasParentMethod(typeInfo, "importJsonFields", _importCache)
}
private static func hasParentMethod(typeInfo: ClassTypeInfo, name: String, cache: HashMap<String, Bool>): Bool {
let key = typeInfo.qualifiedName
if (let Some(cached) <- cache.get(key)) {
return cached
}
let found = try {
if (name == "exportJsonFields") {
typeInfo.getStaticFunction(name, [typeInfo])
} else {
typeInfo.getStaticFunction(name, [typeInfo, TypeInfo.of<HashMap<String, Any>>()])
}
true
} catch (_: Exception) {
false
}
cache[key] = found
found
}
/**
* 获取类型的字段元数据(含缓存)。
* @param typeInfo 目标类类型。
* @param namingPolicy 命名策略。
* @return 字段列表(仅该类自身声明的 var 字段)。
*/
public static func getFields(typeInfo: ClassTypeInfo, namingPolicy: PropertyNamingPolicy): 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()
// @SerializerIgnore
if (v.findAnnotation<SerializerIgnore>().isSome()) {
meta.ignore = true
}
// @SerializerPropertyName 优先
var jsonName = ""
if (let Some(jsonProp) <- v.findAnnotation<SerializerPropertyName>()) {
jsonName = jsonProp.name
} else {
jsonName = applyNamingPolicy(v.name, namingPolicy)
}
meta.jsonName = jsonName
fields.add(meta)
}
_cache[key] = fields
fields
}
/**
* 应用属性命名策略。
* 说明:不剥除字段名前导下划线——`_name` 原样输出 `_name`
* 策略只影响字母大小写(CamelCase 首字母小写、SnakeCase 大写转下划线)。
*/
public static func applyNamingPolicy(fieldName: String, policy: PropertyNamingPolicy): String {
match (policy) {
case PropertyNamingPolicy.Keep => fieldName
case PropertyNamingPolicy.CamelCase => toCamelCase(fieldName)
case PropertyNamingPolicy.SnakeCase => toSnakeCase(fieldName)
}
}
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()
}
}