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,2 @@
|
|||||||
|
target/
|
||||||
|
*.cj.macrocall
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
cjc-version = "1.1.3"
|
||||||
|
name = "simapi_serialization"
|
||||||
|
description = "SimApi 全反射 JSON 序列化库(对齐 .NET JsonSerializer.Serialize/Deserialize + JsonPropertyName/JsonIgnore 特性)"
|
||||||
|
version = "1.0.0"
|
||||||
|
target-dir = ""
|
||||||
|
output-type = "static"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
|
||||||
|
[target]
|
||||||
|
[target.x86_64-w64-mingw32]
|
||||||
|
compile-option = "-Woff unused --diagnostic-format=noColor -Woff deprecated"
|
||||||
|
[target.x86_64-w64-mingw32.bin-dependencies]
|
||||||
|
path-option = [ "${CANGJIE_STDX_PATH}" ]
|
||||||
|
[target.x86_64-unknown-linux-gnu]
|
||||||
|
compile-option = "-Woff unused --diagnostic-format=noColor -Woff deprecated"
|
||||||
|
[target.x86_64-unknown-linux-gnu.bin-dependencies]
|
||||||
|
path-option = [ "${CANGJIE_STDX_PATH}" ]
|
||||||
|
[target.aarch64-unknown-linux-gnu]
|
||||||
|
compile-option = "-Woff unused --diagnostic-format=noColor -Woff deprecated"
|
||||||
|
[target.aarch64-unknown-linux-gnu.bin-dependencies]
|
||||||
|
path-option = [ "${CANGJIE_STDX_PATH}" ]
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||||
|
* JsonOption:序列化选项(对齐 .NET JsonSerializerOptions)。
|
||||||
|
*/
|
||||||
|
|
||||||
|
package simapi_serialization
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 属性命名策略(对齐 .NET JsonNamingPolicy)。
|
||||||
|
*/
|
||||||
|
public enum PropertyNamingPolicy {
|
||||||
|
/// 字段名原样(_username → _username)
|
||||||
|
| None
|
||||||
|
/// 去前导下划线 + 首字母小写(_username → username,对齐 simapi/soulsoft 默认)
|
||||||
|
| CamelCase
|
||||||
|
/// 蛇形(userName → user_name,_username → username)
|
||||||
|
| SnakeCase
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列化选项。
|
||||||
|
*/
|
||||||
|
public class JsonOption {
|
||||||
|
/// 属性命名策略(默认 CamelCase)
|
||||||
|
public var propertyNamingPolicy: PropertyNamingPolicy = PropertyNamingPolicy.CamelCase
|
||||||
|
|
||||||
|
/// 序列化时是否忽略 null/None 字段(默认 false:None 输出 null)
|
||||||
|
public var ignoreNull: Bool = false
|
||||||
|
|
||||||
|
/// 枚举序列化为名字(默认 true;false 暂按名字输出,数字模式后续支持)
|
||||||
|
public var enumAsString: Bool = true
|
||||||
|
|
||||||
|
/// 递归深度上限(默认 64)
|
||||||
|
public var maxDepth: Int64 = 64
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
/// 默认选项实例
|
||||||
|
public static let instance = JsonOption()
|
||||||
|
}
|
||||||
@@ -0,0 +1,243 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||||
|
* JsonReader:JsonValue → 对象(全反射)。
|
||||||
|
*
|
||||||
|
* - 目标类型来自泛型 T(TypeInfo.of<T>())
|
||||||
|
* - 反序列化要求:无参构造 + var 字段(let 只读字段跳过)
|
||||||
|
* - 类型不匹配做宽松转换(JsonInt/JsonFloat/JsonString 互转)
|
||||||
|
*/
|
||||||
|
|
||||||
|
package simapi_serialization
|
||||||
|
|
||||||
|
import std.collection.*
|
||||||
|
import std.convert.*
|
||||||
|
import std.reflect.*
|
||||||
|
import stdx.encoding.json.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JsonValue → 对象 读取器。
|
||||||
|
*/
|
||||||
|
public class JsonReader {
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
public static func read<T>(value: JsonValue, options: JsonOption): T {
|
||||||
|
let result = readValue(value, TypeInfo.of<T>(), options, 0)
|
||||||
|
if (let t: T <- result) {
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
throw Exception("JsonSerializer: 反序列化结果类型不匹配")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readValue(value: JsonValue, typeInfo: TypeInfo, options: JsonOption, depth: Int64): Any {
|
||||||
|
if (depth > options.maxDepth) {
|
||||||
|
throw Exception("JsonSerializer: 超过最大递归深度 ${options.maxDepth}")
|
||||||
|
}
|
||||||
|
let typeName = typeInfo.toString()
|
||||||
|
|
||||||
|
// 基本类型
|
||||||
|
match (typeName) {
|
||||||
|
case "String" => return readString(value)
|
||||||
|
case "Bool" => return readBool(value)
|
||||||
|
case "Int64" => return readInt64(value)
|
||||||
|
case "Int32" => return Int32(readInt64(value))
|
||||||
|
case "Int16" => return Int16(readInt64(value))
|
||||||
|
case "Int8" => return Int8(readInt64(value))
|
||||||
|
case "UInt8" => return UInt8(readInt64(value))
|
||||||
|
case "UInt16" => return UInt16(readInt64(value))
|
||||||
|
case "UInt32" => return UInt32(readInt64(value))
|
||||||
|
case "UInt64" => return UInt64(readInt64(value))
|
||||||
|
case "Float64" => return readFloat64(value)
|
||||||
|
case "Float32" => return Float32(readFloat64(value))
|
||||||
|
case "Rune" => return readChar(value)
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option<X>:null → None;否则读取内层并用枚举构造器包 Some
|
||||||
|
if (typeName.startsWith("Option<")) {
|
||||||
|
if (value is JsonNull) {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
let innerName = JsonWriter.extractTypeArgs(typeName)[0]
|
||||||
|
let innerVal = readValue(value, TypeInfo.get(innerName), options, depth + 1)
|
||||||
|
if (let et: EnumTypeInfo <- typeInfo) {
|
||||||
|
let ctor = et.getConstructor("Some", argsCount: 1)
|
||||||
|
return ctor.apply([innerVal])
|
||||||
|
}
|
||||||
|
return Some(innerVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 集合
|
||||||
|
if (typeName.startsWith("Array<")) {
|
||||||
|
let elemName = JsonWriter.extractTypeArgs(typeName)[0]
|
||||||
|
return readArray(value, elemName, options, depth)
|
||||||
|
}
|
||||||
|
if (typeName.startsWith("std.collection.ArrayList<")) {
|
||||||
|
let elemName = JsonWriter.extractTypeArgs(typeName)[0]
|
||||||
|
return readArrayList(value, elemName, options, depth)
|
||||||
|
}
|
||||||
|
if (typeName.startsWith("std.collection.HashSet<")) {
|
||||||
|
let elemName = JsonWriter.extractTypeArgs(typeName)[0]
|
||||||
|
return readHashSet(value, elemName, options, depth)
|
||||||
|
}
|
||||||
|
if (typeName.startsWith("std.collection.HashMap<")) {
|
||||||
|
let args = JsonWriter.extractTypeArgs(typeName)
|
||||||
|
return readHashMap(value, args[0], args[1], options, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Any / Object → 通用读取
|
||||||
|
if (typeName == "Any" || typeName == "Object") {
|
||||||
|
return jsonToAny(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 枚举:按构造器名创建
|
||||||
|
if (let et: EnumTypeInfo <- typeInfo) {
|
||||||
|
let name = readString(value)
|
||||||
|
return et.construct(name, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对象:反射创建 + 赋值
|
||||||
|
if (let ct: ClassTypeInfo <- typeInfo) {
|
||||||
|
return readObject(value, ct, options, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("JsonSerializer: 不支持的目标类型 ${typeName}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readObject(value: JsonValue, ct: ClassTypeInfo, options: JsonOption, depth: Int64): Any {
|
||||||
|
let instance = ct.construct([])
|
||||||
|
let fields = ReflectionCache.getFields(ct, options)
|
||||||
|
let jobj = value.asObject()
|
||||||
|
for (f in fields) {
|
||||||
|
if (f.ignore) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (!f.mutable) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
match (jobj.get(f.jsonName)) {
|
||||||
|
case Some(jv) =>
|
||||||
|
if (jv is JsonNull) {
|
||||||
|
continue // null:跳过,保持字段默认值
|
||||||
|
}
|
||||||
|
let fieldVal = readValue(jv, f.typeInfo.getOrThrow(), options, depth + 1)
|
||||||
|
f.variable.getOrThrow().setValue(instance, fieldVal)
|
||||||
|
case None => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instance
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Array<X>:经 ArrayList<X> 构造 + add + toArray
|
||||||
|
private static func readArray(value: JsonValue, elemName: String, options: JsonOption, depth: Int64): Any {
|
||||||
|
let listTypeName = "std.collection.ArrayList<${elemName}>"
|
||||||
|
let listCt = (TypeInfo.get(listTypeName) as ClassTypeInfo) ?? throw Exception("JsonSerializer: 无法解析类型 ${listTypeName}")
|
||||||
|
let list = listCt.construct([])
|
||||||
|
let addFunc = listCt.getInstanceFunction("add", [TypeInfo.get(elemName)])
|
||||||
|
let jarr = value.asArray()
|
||||||
|
for (item in jarr.getItems()) {
|
||||||
|
let elem = readValue(item, TypeInfo.get(elemName), options, depth + 1)
|
||||||
|
addFunc.apply(list, [elem])
|
||||||
|
}
|
||||||
|
let toArrayFunc = listCt.getInstanceFunction("toArray", [])
|
||||||
|
toArrayFunc.apply(list, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readArrayList(value: JsonValue, elemName: String, options: JsonOption, depth: Int64): Any {
|
||||||
|
let ct = (TypeInfo.get("std.collection.ArrayList<${elemName}>") as ClassTypeInfo) ?? throw Exception("JsonSerializer: 无法解析 ArrayList<${elemName}>")
|
||||||
|
let list = ct.construct([])
|
||||||
|
let addFunc = ct.getInstanceFunction("add", [TypeInfo.get(elemName)])
|
||||||
|
let jarr = value.asArray()
|
||||||
|
for (item in jarr.getItems()) {
|
||||||
|
let elem = readValue(item, TypeInfo.get(elemName), options, depth + 1)
|
||||||
|
addFunc.apply(list, [elem])
|
||||||
|
}
|
||||||
|
list
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readHashSet(value: JsonValue, elemName: String, options: JsonOption, depth: Int64): Any {
|
||||||
|
let ct = (TypeInfo.get("std.collection.HashSet<${elemName}>") as ClassTypeInfo) ?? throw Exception("JsonSerializer: 无法解析 HashSet<${elemName}>")
|
||||||
|
let set = ct.construct([])
|
||||||
|
let addFunc = ct.getInstanceFunction("add", [TypeInfo.get(elemName)])
|
||||||
|
let jarr = value.asArray()
|
||||||
|
for (item in jarr.getItems()) {
|
||||||
|
let elem = readValue(item, TypeInfo.get(elemName), options, depth + 1)
|
||||||
|
addFunc.apply(set, [elem])
|
||||||
|
}
|
||||||
|
set
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readHashMap(value: JsonValue, keyName: String, valName: String, options: JsonOption,
|
||||||
|
depth: Int64): Any {
|
||||||
|
let ct = (TypeInfo.get("std.collection.HashMap<${keyName}, ${valName}>") as ClassTypeInfo) ?? throw Exception("JsonSerializer: 无法解析 HashMap<${keyName}, ${valName}>")
|
||||||
|
let map = ct.construct([])
|
||||||
|
let addFunc = ct.getInstanceFunction("add", [TypeInfo.get(keyName), TypeInfo.get(valName)])
|
||||||
|
let jobj = value.asObject()
|
||||||
|
for ((k, jv) in jobj.getFields()) {
|
||||||
|
let keyVal = readValue(JsonString(k), TypeInfo.get(keyName), options, depth + 1)
|
||||||
|
let valVal = readValue(jv, TypeInfo.get(valName), options, depth + 1)
|
||||||
|
addFunc.apply(map, [keyVal, valVal])
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}
|
||||||
|
|
||||||
|
/// JsonValue → 通用 Any(动态结构)
|
||||||
|
private static func jsonToAny(v: JsonValue): Any {
|
||||||
|
match (v.kind()) {
|
||||||
|
case JsNull => None
|
||||||
|
case JsBool => v.asBool().getValue()
|
||||||
|
case JsInt => v.asInt().getValue()
|
||||||
|
case JsFloat => v.asFloat().getValue()
|
||||||
|
case JsString => v.asString().getValue()
|
||||||
|
case JsArray =>
|
||||||
|
var list = ArrayList<Any>()
|
||||||
|
for (item in v.asArray().getItems()) {
|
||||||
|
list.add(jsonToAny(item))
|
||||||
|
}
|
||||||
|
list.toArray()
|
||||||
|
case JsObject =>
|
||||||
|
var map = HashMap<String, Any>()
|
||||||
|
for ((k, val) in v.asObject().getFields()) {
|
||||||
|
map[k] = jsonToAny(val)
|
||||||
|
}
|
||||||
|
map
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 宽松类型转换 =====
|
||||||
|
|
||||||
|
private static func readString(v: JsonValue): String {
|
||||||
|
if (let s: JsonString <- v) { return s.getValue() }
|
||||||
|
if (let i: JsonInt <- v) { return "${i.getValue()}" }
|
||||||
|
if (let f: JsonFloat <- v) { return "${f.getValue()}" }
|
||||||
|
if (let b: JsonBool <- v) { return "${b.getValue()}" }
|
||||||
|
throw Exception("JsonSerializer: 无法转换为 String")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readInt64(v: JsonValue): Int64 {
|
||||||
|
if (let i: JsonInt <- v) { return i.getValue() }
|
||||||
|
if (let f: JsonFloat <- v) { return Int64(f.getValue()) }
|
||||||
|
if (let s: JsonString <- v) { return Int64.parse(s.getValue()) }
|
||||||
|
throw Exception("JsonSerializer: 无法转换为 Int64")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readFloat64(v: JsonValue): Float64 {
|
||||||
|
if (let f: JsonFloat <- v) { return f.getValue() }
|
||||||
|
if (let i: JsonInt <- v) { return Float64(i.getValue()) }
|
||||||
|
if (let s: JsonString <- v) { return Float64.parse(s.getValue()) }
|
||||||
|
throw Exception("JsonSerializer: 无法转换为 Float64")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readBool(v: JsonValue): Bool {
|
||||||
|
if (let b: JsonBool <- v) { return b.getValue() }
|
||||||
|
if (let s: JsonString <- v) { return s.getValue() == "true" }
|
||||||
|
throw Exception("JsonSerializer: 无法转换为 Bool")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func readChar(v: JsonValue): Rune {
|
||||||
|
let s = readString(v)
|
||||||
|
for (r in s.runes()) {
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
throw Exception("JsonSerializer: 无法转换为 Char")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||||
|
* JsonSerializer:公开 API,对齐 .NET System.Text.Json:
|
||||||
|
* JsonSerializer.Serialize(obj, JsonOption) → JSON 字符串
|
||||||
|
* JsonSerializer.Deserialize<T>(jsonString, JsonOption) → T
|
||||||
|
*
|
||||||
|
* 全反射实现:任意类(无参构造 + var 字段)无需实现接口、无需宏。
|
||||||
|
*/
|
||||||
|
|
||||||
|
package simapi_serialization
|
||||||
|
|
||||||
|
import stdx.encoding.json.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全反射 JSON 序列化器(对齐 .NET JsonSerializer)。
|
||||||
|
*/
|
||||||
|
public class JsonSerializer {
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列化任意对象为 JSON 字符串。
|
||||||
|
* @param obj 任意对象(基础类型/集合/HashMap/枚举/普通类)。
|
||||||
|
* @param options 序列化选项(默认 JsonOption.instance)。
|
||||||
|
* @return JSON 字符串。
|
||||||
|
*/
|
||||||
|
public static func Serialize(obj: Any, options: JsonOption): String {
|
||||||
|
JsonWriter.write(obj, options).toJsonString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 序列化任意对象为 JSON 字符串(默认选项)。
|
||||||
|
*/
|
||||||
|
public static func Serialize(obj: Any): String {
|
||||||
|
Serialize(obj, JsonOption.instance)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反序列化 JSON 字符串为目标类型。
|
||||||
|
* @param jsonString JSON 字符串。
|
||||||
|
* @param options 序列化选项(默认 JsonOption.instance)。
|
||||||
|
* @return T 实例(要求 T 有无参构造,字段为 var)。
|
||||||
|
*/
|
||||||
|
public static func Deserialize<T>(jsonString: String, options: JsonOption): T {
|
||||||
|
let value = JsonValue.fromStr(jsonString)
|
||||||
|
JsonReader.read<T>(value, options)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反序列化 JSON 字符串为目标类型(默认选项)。
|
||||||
|
*/
|
||||||
|
public static func Deserialize<T>(jsonString: String): T {
|
||||||
|
Deserialize<T>(jsonString, JsonOption.instance)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||||
|
* JsonWriter:对象 → JsonValue(全反射)。
|
||||||
|
*
|
||||||
|
* 集合处理:仓颉泛型是不变的(Array<String> 不能 cast 为 Array<Any>),
|
||||||
|
* 因此通过反射调用 iterator()/hasNext()/next() 与 keys()/get() 迭代元素。
|
||||||
|
*/
|
||||||
|
|
||||||
|
package simapi_serialization
|
||||||
|
|
||||||
|
import std.collection.*
|
||||||
|
import std.reflect.*
|
||||||
|
import stdx.encoding.json.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 对象 → JsonValue 写入器。
|
||||||
|
*/
|
||||||
|
public class JsonWriter {
|
||||||
|
private init() {}
|
||||||
|
|
||||||
|
public static func write(obj: Any, options: JsonOption): JsonValue {
|
||||||
|
writeValue(obj, options, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func writeValue(value: Any, options: JsonOption, depth: Int64): JsonValue {
|
||||||
|
if (depth > options.maxDepth) {
|
||||||
|
throw Exception("JsonSerializer: 超过最大递归深度 ${options.maxDepth}")
|
||||||
|
}
|
||||||
|
// 基本类型(快路径)
|
||||||
|
if (let s: String <- value) { return JsonString(s) }
|
||||||
|
if (let b: Bool <- value) { return JsonBool(b) }
|
||||||
|
if (let i: Int64 <- value) { return JsonInt(i) }
|
||||||
|
if (let f: Float64 <- value) { return JsonFloat(f) }
|
||||||
|
if (let i32: Int32 <- value) { return JsonInt(Int64(i32)) }
|
||||||
|
if (let i16: Int16 <- value) { return JsonInt(Int64(i16)) }
|
||||||
|
if (let i8: Int8 <- value) { return JsonInt(Int64(i8)) }
|
||||||
|
if (let u8: UInt8 <- value) { return JsonInt(Int64(u8)) }
|
||||||
|
if (let u16: UInt16 <- value) { return JsonInt(Int64(u16)) }
|
||||||
|
if (let u32: UInt32 <- value) { return JsonInt(Int64(u32)) }
|
||||||
|
if (let u64: UInt64 <- value) { return JsonInt(Int64(u64)) }
|
||||||
|
if (let f32: Float32 <- value) { return JsonFloat(Float64(f32)) }
|
||||||
|
if (let c: Rune <- value) { return JsonString("${c}") }
|
||||||
|
|
||||||
|
let typeName = TypeInfo.of(value).toString()
|
||||||
|
|
||||||
|
// Option 值(Some(x) / None):仓颉 Option 是泛型枚举,用 destruct 取关联值
|
||||||
|
if (typeName.startsWith("Option<")) {
|
||||||
|
if (let Some(inner) <- unwrapOptionValue(value)) {
|
||||||
|
return writeValue(inner, options, depth + 1)
|
||||||
|
}
|
||||||
|
return JsonNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 集合:Array / ArrayList / HashSet(统一反射迭代)
|
||||||
|
if (typeName.startsWith("Array<") || typeName.startsWith("std.collection.ArrayList<") ||
|
||||||
|
typeName.startsWith("std.collection.HashSet<")) {
|
||||||
|
return writeCollection(value, options, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashMap<String, V>
|
||||||
|
if (typeName.startsWith("std.collection.HashMap<")) {
|
||||||
|
return writeHashMap(value, options, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
let ti2 = TypeInfo.of(value)
|
||||||
|
|
||||||
|
// 枚举:destruct 取构造器名
|
||||||
|
if (let et: EnumTypeInfo <- ti2) {
|
||||||
|
let (ctor, _) = et.destruct(value)
|
||||||
|
return JsonString(ctor.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对象:反射字段
|
||||||
|
if (let ct: ClassTypeInfo <- ti2) {
|
||||||
|
return writeObject(value, ct, options, depth)
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("JsonSerializer: 不支持的类型 ${typeName}")
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func writeCollection(value: Any, options: JsonOption, depth: Int64): JsonValue {
|
||||||
|
let arr = JsonArray()
|
||||||
|
forEachElement(value) { elem =>
|
||||||
|
arr.add(writeValue(elem, options, depth + 1))
|
||||||
|
}
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func writeHashMap(value: Any, options: JsonOption, depth: Int64): JsonValue {
|
||||||
|
let obj = JsonObject()
|
||||||
|
let ti = TypeInfo.of(value)
|
||||||
|
let typeName = ti.toString()
|
||||||
|
let args = extractTypeArgs(typeName)
|
||||||
|
let keyType = if (args.size > 0) { TypeInfo.get(args[0]) } else { TypeInfo.of<String>() }
|
||||||
|
let getFunc = ti.getInstanceFunction("get", [keyType])
|
||||||
|
let keys = ti.getInstanceFunction("keys", []).apply(value, [])
|
||||||
|
forEachElement(keys) { k =>
|
||||||
|
let v = getFunc.apply(value, [k])
|
||||||
|
obj.put(toKeyString(k), writeValue(v, options, depth + 1))
|
||||||
|
}
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func writeObject(value: Any, ct: ClassTypeInfo, options: JsonOption, depth: Int64): JsonValue {
|
||||||
|
let obj = JsonObject()
|
||||||
|
let fields = ReflectionCache.getFields(ct, options)
|
||||||
|
for (f in fields) {
|
||||||
|
if (f.ignore) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
let raw = f.variable.getOrThrow().getValue(value)
|
||||||
|
if (options.ignoreNull && isNoneValue(raw)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
obj.put(f.jsonName, writeValue(raw, options, depth + 1))
|
||||||
|
}
|
||||||
|
obj
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 反射迭代集合:优先 toArray 转数组再 get(index);无 toArray(如 Array)直接用原集合
|
||||||
|
private static func forEachElement(collection: Any, action: (Any) -> Unit): Unit {
|
||||||
|
let ti = TypeInfo.of(collection)
|
||||||
|
var arr: Any = collection
|
||||||
|
try {
|
||||||
|
arr = ti.getInstanceFunction("toArray", []).apply(collection, [])
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// 无 toArray(如 Array):直接用原集合 get(i)
|
||||||
|
}
|
||||||
|
let arrTi = TypeInfo.of(arr)
|
||||||
|
// 仓颉集合 get(index) 返回 Option<T>:Some(元素) / None(越界结束)
|
||||||
|
let getFunc = arrTi.getInstanceFunction("get", [TypeInfo.of<Int64>()])
|
||||||
|
var i: Int64 = 0
|
||||||
|
var guard: Int64 = 0
|
||||||
|
while (true) {
|
||||||
|
guard += 1
|
||||||
|
if (guard > 1000000) {
|
||||||
|
throw Exception("JsonSerializer: 集合迭代超限 type=${TypeInfo.of(arr)}")
|
||||||
|
}
|
||||||
|
var elem: ?Any = None
|
||||||
|
try {
|
||||||
|
elem = Some(getFunc.apply(arr, [i]))
|
||||||
|
} catch (_: Exception) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (let Some(v) <- unwrapOptionValue(elem.getOrThrow())) {
|
||||||
|
action(v)
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
i += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 判断 Any 值是否为 None(Option 枚举的 None 构造器)
|
||||||
|
private static func isNoneValue(v: Any): Bool {
|
||||||
|
unwrapOptionValue(v).isNone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 若值为 Option 的 Some(x) 返回 Some(x);None 或非 Option 返回 None
|
||||||
|
private static func unwrapOptionValue(v: Any): ?Any {
|
||||||
|
if (let et: EnumTypeInfo <- TypeInfo.of(v)) {
|
||||||
|
let (ctor, values) = et.destruct(v)
|
||||||
|
if (ctor.name == "Some" && values.size > 0) {
|
||||||
|
return Some(values[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func toKeyString(k: Any): String {
|
||||||
|
if (let s: String <- k) {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
if (let s: ToString <- k) {
|
||||||
|
return s.toString()
|
||||||
|
}
|
||||||
|
TypeInfo.of(k).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 从 "Array<String>" / "std.collection.HashMap<String, Any>" / "Option<Int64>" 提取类型参数
|
||||||
|
public static func extractTypeArgs(typeName: String): ArrayList<String> {
|
||||||
|
var args = ArrayList<String>()
|
||||||
|
match (typeName.indexOf("<")) {
|
||||||
|
case None => return args
|
||||||
|
case Some(i) =>
|
||||||
|
var depth: Int64 = 0
|
||||||
|
var start = i + 1
|
||||||
|
var j = i + 1
|
||||||
|
while (j < typeName.size) {
|
||||||
|
let b = typeName[j]
|
||||||
|
if (b == 0x3Cu8) { // '<'
|
||||||
|
depth += 1
|
||||||
|
} else if (b == 0x3Eu8) { // '>'
|
||||||
|
if (depth == 0) {
|
||||||
|
args.add(typeName[start..j])
|
||||||
|
return args
|
||||||
|
}
|
||||||
|
depth -= 1
|
||||||
|
} else if (b == 0x2Cu8) { // ','
|
||||||
|
if (depth == 0) {
|
||||||
|
args.add(typeName[start..j])
|
||||||
|
start = j + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
args
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||||
|
* 对齐 .NET System.Text.Json.Serialization 特性:
|
||||||
|
* - [JsonPropertyName("xxx")] → @JsonPropertyName["xxx"] 指定字段的 JSON 名称
|
||||||
|
* - [JsonIgnore] → @JsonIgnore 序列化/反序列化时忽略该字段
|
||||||
|
*
|
||||||
|
* 参考仓颉官方注解用法(std.reflect 运行时读取):
|
||||||
|
* https://docs.cangjie-lang.cn/en/docs/1.0.0/libs/std/reflect/reflect_samples/annotation.html
|
||||||
|
*/
|
||||||
|
|
||||||
|
package simapi_serialization.attributes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定字段的 JSON 名称(对齐 .NET JsonPropertyNameAttribute)。
|
||||||
|
* 用法:@JsonPropertyName["user_name"] public var _username: String = ""
|
||||||
|
*/
|
||||||
|
@Annotation[target: [MemberVariable, MemberProperty]]
|
||||||
|
public class JsonPropertyName {
|
||||||
|
public let name: String
|
||||||
|
|
||||||
|
public const init(name: String) {
|
||||||
|
this.name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标记字段不做 JSON 处理(对齐 .NET JsonIgnoreAttribute)。
|
||||||
|
* 用法:@JsonIgnore public var _temp: String = ""
|
||||||
|
*/
|
||||||
|
@Annotation[target: [MemberVariable, MemberProperty]]
|
||||||
|
public class JsonIgnore {
|
||||||
|
public const init() {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user