feat: SimApiSerialization 全反射 JSON 序列化库 v1
对齐 .NET System.Text.Json: - JsonSerializer.Serialize(obj, JsonOption) / Deserialize<T>(jsonString, JsonOption) - @JsonPropertyName / @JsonIgnore 特性(运行时注解反射) - JsonOption:PropertyNamingPolicy(None/CamelCase/SnakeCase)、ignoreNull、maxDepth - 全反射实现:基础类型/枚举/Option/Array/ArrayList/HashSet/HashMap/嵌套对象 - 仓颉特性适配: * Option 是泛型枚举 → EnumTypeInfo.destruct/construct 处理 * 集合 get(i) 返回 Option<T> → Some/None 解包迭代 * 集合泛型不变 → 反射方法调用(toArray/get/add/keys)而非 cast * as 返回 Option → ?? 解包;JsonKind 用 is 判断 - 已知限制:反序列化需无参构造+var 字段;父类字段不序列化(平台严格类型校验); 枚举按名字;循环引用用深度上限保护 - 已实测:User 完整回环 + 数组/HashMap 回环 + 特性生效
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||
* ReflectionCache:字段元数据(含继承链、命名策略、@JsonPropertyName/@JsonIgnore)与缓存。
|
||||
*
|
||||
* 已知限制(平台级):
|
||||
* - InstanceVariableInfo.getValue/setValue 有「声明类严格类型校验」,父类字段无法用子类实例读写,
|
||||
* 故 v1 仅枚举运行时类自身声明的 var 字段(父类字段暂不序列化)。
|
||||
*/
|
||||
|
||||
package simapi_serialization
|
||||
|
||||
import std.collection.*
|
||||
import std.reflect.*
|
||||
import simapi_serialization.attributes.*
|
||||
|
||||
/**
|
||||
* 单个字段的序列化元数据。
|
||||
*/
|
||||
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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user