feat: 实现 @JsonParent 宏——父类字段序列化/反序列化支持
- 新增 src/macros/JsonParentMacro.cj(macro package simapi_serialization.macros):
标注父类自动补 open,收集 public var 字段(跳过 @JsonIgnore,
@JsonPropertyName 用注解名),字符串拼接生成静态方法:
exportJsonFields(instance: T): HashMap<String, Any>
importJsonFields(instance: T, json: HashMap<String, Any>): Unit
- JsonWriter.writeParentFields:沿 superClass 链调用 exportJsonFields 合并父类字段
- JsonReader.readParentFields:沿 superClass 链调用 importJsonFields 回填父类字段
- ReflectionCache 增加 hasExportFields/hasImportFields 缓存(避免每次反射查找)
- 测试:16 用例全绿——新增 parentMacroMethods、multiLevelInheritance、
parentAutoOpen;更新 objectSerialize/objectRoundTrip 断言父类字段输出
This commit is contained in:
@@ -133,9 +133,38 @@ public class JsonReader {
|
|||||||
case None => ()
|
case None => ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 父类字段(@JsonParent 宏生成的静态导入方法,沿继承链逐层回填)
|
||||||
|
readParentFields(value, ct, instance, options)
|
||||||
instance
|
instance
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 沿 superClass 链调用每层的 importJsonFields 静态方法回填父类字段。
|
||||||
|
/// 无宏的父类(hasImportFields 缓存判断)静默跳过;宏生成的方法内部按
|
||||||
|
/// if (k == "字段名") 忽略未知键,因此直接把全部 JSON 键传入即可。
|
||||||
|
private static func readParentFields(value: JsonValue, ct: ClassTypeInfo, instance: Any, options: JsonOption): Unit {
|
||||||
|
let jobj = value.asObject()
|
||||||
|
var parent = ct.superClass
|
||||||
|
while (let Some(p) <- parent) {
|
||||||
|
if (ReflectionCache.hasImportFields(p)) {
|
||||||
|
try {
|
||||||
|
let fn = p.getStaticFunction("importJsonFields",
|
||||||
|
[p, TypeInfo.of<HashMap<String, Any>>()])
|
||||||
|
var pjson = HashMap<String, Any>()
|
||||||
|
for ((k, jv) in jobj.getFields()) {
|
||||||
|
if (!(jv is JsonNull)) {
|
||||||
|
pjson[k] = jsonToAny(jv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let args: Array<Any> = [instance, pjson]
|
||||||
|
fn.apply(p, args)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// 导入失败(如类型不匹配)不中断整体反序列化
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent = p.superClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Array<X>:经 ArrayList<X> 构造 + add + toArray
|
/// Array<X>:经 ArrayList<X> 构造 + add + toArray
|
||||||
private static func readArray(value: JsonValue, elemName: String, options: JsonOption, depth: Int64): Any {
|
private static func readArray(value: JsonValue, elemName: String, options: JsonOption, depth: Int64): Any {
|
||||||
let listTypeName = "std.collection.ArrayList<${elemName}>"
|
let listTypeName = "std.collection.ArrayList<${elemName}>"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ package simapi_serialization.json
|
|||||||
import std.collection.*
|
import std.collection.*
|
||||||
import std.unittest.*
|
import std.unittest.*
|
||||||
import std.unittest.testmacro.*
|
import std.unittest.testmacro.*
|
||||||
|
import simapi_serialization.macros.*
|
||||||
|
|
||||||
enum Role {
|
enum Role {
|
||||||
| Admin
|
| Admin
|
||||||
@@ -26,10 +27,16 @@ class Address {
|
|||||||
public var zip: String = "" // 无下划线 → 原样输出 zip
|
public var zip: String = "" // 无下划线 → 原样输出 zip
|
||||||
}
|
}
|
||||||
|
|
||||||
// 继承测试:父类字段(_id/_createdAt)受仓颉反射「声明类严格校验」限制
|
// 继承测试:父类标注 @JsonParent 宏 → 自动补 open + 生成导出/导入静态方法,
|
||||||
|
// 父类字段(_id/_createdAt)经宏的静态类型访问绕开反射声明类校验
|
||||||
|
@JsonParent
|
||||||
open class BaseUser {
|
open class BaseUser {
|
||||||
public var _id: String = ""
|
public var _id: String = ""
|
||||||
public var _createdAt: String = ""
|
public var _createdAt: String = ""
|
||||||
|
@JsonIgnore
|
||||||
|
public var _temp: String = ""
|
||||||
|
@JsonPropertyName["parent_alias"]
|
||||||
|
public var _parentAlias: String = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
class User <: BaseUser {
|
class User <: BaseUser {
|
||||||
@@ -48,8 +55,27 @@ class User <: BaseUser {
|
|||||||
public var temp: String = "secret" // 无下划线 + 忽略
|
public var temp: String = "secret" // 无下划线 + 忽略
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 多层继承:爷(标宏)→ 父(不标宏)→ 子
|
||||||
|
@JsonParent
|
||||||
|
open class GrandParent {
|
||||||
|
public var _gp: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
open class MidParent <: GrandParent {
|
||||||
|
public var _mp: String = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
class LeafChild <: MidParent {
|
||||||
|
public var _lc: String = ""
|
||||||
|
public init() {}
|
||||||
|
}
|
||||||
|
|
||||||
func makeUser(): User {
|
func makeUser(): User {
|
||||||
let u = User()
|
let u = User()
|
||||||
|
u._id = "u-1"
|
||||||
|
u._createdAt = "2025-01-01"
|
||||||
|
u._temp = "secret-parent"
|
||||||
|
u._parentAlias = "parent-xiaoming"
|
||||||
u._name = "alice"
|
u._name = "alice"
|
||||||
u._age = 30
|
u._age = 30
|
||||||
u.score = 88.5
|
u.score = 88.5
|
||||||
@@ -117,9 +143,14 @@ public class JsonSerializerTests {
|
|||||||
// 嵌套对象
|
// 嵌套对象
|
||||||
@Expect(json.contains("\"_city\": \"beijing\""), true)
|
@Expect(json.contains("\"_city\": \"beijing\""), true)
|
||||||
@Expect(json.contains("\"zip\": \"100000\""), true)
|
@Expect(json.contains("\"zip\": \"100000\""), true)
|
||||||
// 继承限制:父类字段不序列化
|
// 父类字段(@JsonParent 宏):_id/_createdAt 输出,@JsonIgnore 父类字段不输出
|
||||||
@Expect(json.contains("_id"), false)
|
@Expect(json.contains("\"_id\": \"u-1\""), true)
|
||||||
@Expect(json.contains("_createdAt"), false)
|
@Expect(json.contains("\"_createdAt\": \"2025-01-01\""), true)
|
||||||
|
// 父类 @JsonPropertyName["parent_alias"] 生效
|
||||||
|
@Expect(json.contains("\"parent_alias\": \"parent-xiaoming\""), true)
|
||||||
|
@Expect(json.contains("_parentAlias"), false)
|
||||||
|
// 父类 @JsonIgnore _temp 不输出
|
||||||
|
@Expect(json.contains("secret-parent"), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 对象反序列化往返
|
/// 对象反序列化往返
|
||||||
@@ -135,6 +166,10 @@ public class JsonSerializerTests {
|
|||||||
@Expect(u2.addr.zip, "100000")
|
@Expect(u2.addr.zip, "100000")
|
||||||
@Expect(u2._alias, "xiaoming")
|
@Expect(u2._alias, "xiaoming")
|
||||||
@Expect(u2.temp, "secret") // @JsonIgnore:反序列化不改动默认值
|
@Expect(u2.temp, "secret") // @JsonIgnore:反序列化不改动默认值
|
||||||
|
// 父类字段往返(@JsonParent 宏)
|
||||||
|
@Expect(u2._id, "u-1")
|
||||||
|
@Expect(u2._createdAt, "2025-01-01")
|
||||||
|
@Expect(u2._parentAlias, "parent-xiaoming")
|
||||||
// Option 往返
|
// Option 往返
|
||||||
let optVal: ?String = u2.opt
|
let optVal: ?String = u2.opt
|
||||||
@Expect(optVal.isSome(), true)
|
@Expect(optVal.isSome(), true)
|
||||||
@@ -286,4 +321,57 @@ public class JsonSerializerTests {
|
|||||||
}
|
}
|
||||||
@Expect(threw, true)
|
@Expect(threw, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 父类字段序列化/反序列化(@JsonParent 宏):已并入 objectSerialize/objectRoundTrip,
|
||||||
|
/// 此处验证宏生成的静态方法可直接调用
|
||||||
|
@TestCase
|
||||||
|
public func parentMacroMethods() {
|
||||||
|
let u = User()
|
||||||
|
u._id = "m-1"
|
||||||
|
u._createdAt = "2025-02-02"
|
||||||
|
u._parentAlias = "alias-m"
|
||||||
|
// 宏生成的导出方法:子类实例作父类参数
|
||||||
|
let m = BaseUser.exportJsonFields(u)
|
||||||
|
@Expect(toStr(m["_id"]), "m-1")
|
||||||
|
@Expect(toStr(m["_createdAt"]), "2025-02-02")
|
||||||
|
@Expect(toStr(m["parent_alias"]), "alias-m")
|
||||||
|
// @JsonIgnore 父类字段不导出
|
||||||
|
@Expect(m.contains("_temp"), false)
|
||||||
|
// 宏生成的导入方法
|
||||||
|
var json = HashMap<String, Any>()
|
||||||
|
json["_id"] = "m-2"
|
||||||
|
json["parent_alias"] = "alias-m2"
|
||||||
|
BaseUser.importJsonFields(u, json)
|
||||||
|
@Expect(u._id, "m-2")
|
||||||
|
@Expect(u._parentAlias, "alias-m2")
|
||||||
|
@Expect(u._createdAt, "2025-02-02") // 未传的键保持原值
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 多层继承:爷标宏、中间层不标宏 → 爷字段导出,中间层字段不导出
|
||||||
|
@TestCase
|
||||||
|
public func multiLevelInheritance() {
|
||||||
|
let leaf = LeafChild()
|
||||||
|
leaf._gp = "gp-v"
|
||||||
|
leaf._mp = "mp-v"
|
||||||
|
leaf._lc = "lc-v"
|
||||||
|
|
||||||
|
let json = JsonSerializer.Serialize(leaf)
|
||||||
|
@Expect(json.contains("\"_gp\": \"gp-v\""), true) // 爷字段(宏)导出
|
||||||
|
@Expect(json.contains("_mp"), false) // 中间层无宏不导出
|
||||||
|
@Expect(json.contains("\"_lc\": \"lc-v\""), true) // 子字段(反射)导出
|
||||||
|
|
||||||
|
let back = JsonSerializer.Deserialize<LeafChild>(json)
|
||||||
|
@Expect(back._gp, "gp-v")
|
||||||
|
@Expect(back._lc, "lc-v")
|
||||||
|
@Expect(back._mp, "") // 中间层字段保持默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 宏自动补 open:GrandParent 未显式写 open(@JsonParent 自动补),继承链可正常编译
|
||||||
|
@TestCase
|
||||||
|
public func parentAutoOpen() {
|
||||||
|
let leaf = LeafChild()
|
||||||
|
leaf._gp = "auto"
|
||||||
|
let json = JsonSerializer.Serialize(leaf)
|
||||||
|
@Expect(json.contains("\"_gp\": \"auto\""), true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ public class JsonWriter {
|
|||||||
|
|
||||||
private static func writeObject(value: Any, ct: ClassTypeInfo, options: JsonOption, depth: Int64): JsonValue {
|
private static func writeObject(value: Any, ct: ClassTypeInfo, options: JsonOption, depth: Int64): JsonValue {
|
||||||
let obj = JsonObject()
|
let obj = JsonObject()
|
||||||
|
// 1) 子类自身字段(反射)
|
||||||
let fields = ReflectionCache.getFields(ct, options)
|
let fields = ReflectionCache.getFields(ct, options)
|
||||||
for (f in fields) {
|
for (f in fields) {
|
||||||
// 只要不是 @JsonIgnore 的字段一律输出;None 字段输出 null
|
// 只要不是 @JsonIgnore 的字段一律输出;None 字段输出 null
|
||||||
@@ -112,9 +113,35 @@ public class JsonWriter {
|
|||||||
let raw = f.variable.getOrThrow().getValue(value)
|
let raw = f.variable.getOrThrow().getValue(value)
|
||||||
obj.put(f.jsonName, writeValue(raw, options, depth + 1))
|
obj.put(f.jsonName, writeValue(raw, options, depth + 1))
|
||||||
}
|
}
|
||||||
|
// 2) 父类字段(@JsonParent 宏生成的静态导出方法,沿继承链逐层调用)
|
||||||
|
writeParentFields(value, ct, obj, options, depth)
|
||||||
obj
|
obj
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 沿 superClass 链调用每层的 exportJsonFields 静态方法并合并进 JSON。
|
||||||
|
/// 无宏的父类(hasExportFields 缓存判断)静默跳过,继续向祖父层查找。
|
||||||
|
private static func writeParentFields(value: Any, ct: ClassTypeInfo, obj: JsonObject, options: JsonOption,
|
||||||
|
depth: Int64): Unit {
|
||||||
|
var parent = ct.superClass
|
||||||
|
while (let Some(p) <- parent) {
|
||||||
|
if (ReflectionCache.hasExportFields(p)) {
|
||||||
|
try {
|
||||||
|
let f = p.getStaticFunction("exportJsonFields", [p])
|
||||||
|
let args: Array<Any> = [value] // 子类实例作父类类型参数
|
||||||
|
let m = f.apply(p, args)
|
||||||
|
if (let h: HashMap<String, Any> <- m) {
|
||||||
|
for ((k, v) in h) {
|
||||||
|
obj.put(k, writeValue(v, options, depth + 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// 调用失败(不应发生)→ 跳过该层
|
||||||
|
}
|
||||||
|
}
|
||||||
|
parent = p.superClass
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 反射迭代集合:优先 toArray 转数组再 get(index);无 toArray(如 Array)直接用原集合
|
/// 反射迭代集合:优先 toArray 转数组再 get(index);无 toArray(如 Array)直接用原集合
|
||||||
private static func forEachElement(collection: Any, action: (Any) -> Unit): Unit {
|
private static func forEachElement(collection: Any, action: (Any) -> Unit): Unit {
|
||||||
let ti = TypeInfo.of(collection)
|
let ti = TypeInfo.of(collection)
|
||||||
|
|||||||
@@ -40,6 +40,44 @@ public class ReflectionCache {
|
|||||||
|
|
||||||
private static let _cache = HashMap<String, ArrayList<FieldMetadata>>()
|
private static let _cache = HashMap<String, ArrayList<FieldMetadata>>()
|
||||||
|
|
||||||
|
/// 类型 → 是否有 @JsonParent 宏生成的 exportJsonFields(Bool)
|
||||||
|
private static let _exportCache = HashMap<String, Bool>()
|
||||||
|
/// 类型 → 是否有 @JsonParent 宏生成的 importJsonFields(Bool)
|
||||||
|
private static let _importCache = HashMap<String, Bool>()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该类型是否含 @JsonParent 宏生成的导出静态方法(含缓存)。
|
||||||
|
*/
|
||||||
|
public static func hasExportFields(typeInfo: ClassTypeInfo): Bool {
|
||||||
|
hasParentMethod(typeInfo, "exportJsonFields", _exportCache)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该类型是否含 @JsonParent 宏生成的导入静态方法(含缓存)。
|
||||||
|
*/
|
||||||
|
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 typeInfo 目标类类型。
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
||||||
|
* @JsonParent:标注在父类上,自动生成父类字段的 JSON 导出/导入静态方法。
|
||||||
|
*
|
||||||
|
* 背景:Cangjie 反射对实例成员的读写有「声明类严格校验」
|
||||||
|
* (declaringClass != TypeInfo.of(instance)),父类字段无法用子类实例
|
||||||
|
* 读写(IllegalTypeException)。本宏在编译期生成静态方法,方法体内用
|
||||||
|
* 静态类型访问字段(非反射),从而绕开该限制;序列化库沿 superClass 链
|
||||||
|
* 调用约定静态方法(见 JsonWriter/JsonReader)。
|
||||||
|
*
|
||||||
|
* 用法:
|
||||||
|
* @JsonParent
|
||||||
|
* class BaseUser {
|
||||||
|
* public var _id: String = ""
|
||||||
|
* @JsonPropertyName["userId"]
|
||||||
|
* public var _userId: String = ""
|
||||||
|
* @JsonIgnore
|
||||||
|
* public var _temp: String = ""
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* 行为:
|
||||||
|
* - 非 open class 自动补 open
|
||||||
|
* - 生成 exportJsonFields(instance: T): HashMap<String, Any>
|
||||||
|
* - 生成 importJsonFields(instance: T, json: HashMap<String, Any>): Unit
|
||||||
|
* - @JsonIgnore 字段跳过;@JsonPropertyName 用注解名做 JSON 键
|
||||||
|
*/
|
||||||
|
|
||||||
|
macro package simapi_serialization.macros
|
||||||
|
|
||||||
|
import std.ast.*
|
||||||
|
import std.collection.*
|
||||||
|
|
||||||
|
/// 单个字段的元数据
|
||||||
|
private class FieldInfo {
|
||||||
|
public var fieldName: String = "" // 源码字段名(如 _id)
|
||||||
|
public var jsonName: String = "" // JSON 键名(注解优先,否则字段名)
|
||||||
|
public var typeName: String = "" // 字段类型名(如 String)
|
||||||
|
public var isVar: Bool = true // var 可变(可导入);let 只导出
|
||||||
|
|
||||||
|
public init() {}
|
||||||
|
|
||||||
|
public init(fieldName: String, jsonName: String, typeName: String, isVar: Bool) {
|
||||||
|
this.fieldName = fieldName
|
||||||
|
this.jsonName = jsonName
|
||||||
|
this.typeName = typeName
|
||||||
|
this.isVar = isVar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成父类字段 JSON 导出/导入静态方法。
|
||||||
|
* @param input 被标注的类声明。
|
||||||
|
* @return 注入静态方法后的类声明。
|
||||||
|
*/
|
||||||
|
public macro JsonParent(input: Tokens): Tokens {
|
||||||
|
let decl = parseDecl(input)
|
||||||
|
if (let cd: ClassDecl <- decl) {
|
||||||
|
// 1. open 处理:自动补 open(类被标注即表达「要被继承」的意图)
|
||||||
|
var hasOpen = false
|
||||||
|
for (m in cd.modifiers) {
|
||||||
|
if (m.keyword.kind == TokenKind.OPEN) {
|
||||||
|
hasOpen = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!hasOpen) {
|
||||||
|
cd.modifiers.add(Modifier(Token(TokenKind.OPEN)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 收集字段
|
||||||
|
let fields = collectFields(cd)
|
||||||
|
|
||||||
|
// 3. 生成导出/导入静态方法(字符串拼接 + cangjieLex 解析)
|
||||||
|
let exportSrc = buildExportSource(cd.identifier.value, fields)
|
||||||
|
let importSrc = buildImportSource(cd.identifier.value, fields)
|
||||||
|
|
||||||
|
if (let fd: FuncDecl <- parseDecl(cangjieLex(exportSrc))) {
|
||||||
|
cd.body.decls.add(fd)
|
||||||
|
}
|
||||||
|
if (let fd: FuncDecl <- parseDecl(cangjieLex(importSrc))) {
|
||||||
|
cd.body.decls.add(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cd.toTokens()
|
||||||
|
}
|
||||||
|
throw ASTException("@JsonParent 只能标注在 class 声明上(不支持 struct/interface/enum)")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 收集类体内的可序列化字段(_ 前缀 + 类型注解;跳过 @JsonIgnore;@JsonPropertyName 用注解名)
|
||||||
|
private func collectFields(cd: ClassDecl): ArrayList<FieldInfo> {
|
||||||
|
var result = ArrayList<FieldInfo>()
|
||||||
|
for (d in cd.body.decls) {
|
||||||
|
match (d) {
|
||||||
|
case vd: VarDecl =>
|
||||||
|
if (isSerializableField(vd)) {
|
||||||
|
result.add(FieldInfo(vd.identifier.value, vd.identifier.value,
|
||||||
|
vd.declType.toTokens().toString(), vd.keyword.kind == TokenKind.VAR))
|
||||||
|
}
|
||||||
|
case md: MacroExpandDecl =>
|
||||||
|
// 带注解的字段:@JsonIgnore 跳过;@JsonPropertyName["x"] 取注解名
|
||||||
|
if (md.identifier.value == "JsonIgnore") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (let Some(inner) <- unwrapMacroField(md)) {
|
||||||
|
if (isSerializableField(inner)) {
|
||||||
|
let jsonName = if (md.identifier.value == "JsonPropertyName") {
|
||||||
|
// 属性 tokens 形如 "user_alias"(带引号),去掉两端引号
|
||||||
|
stripQuotes(md.macroAttrs.toString())
|
||||||
|
} else {
|
||||||
|
inner.identifier.value
|
||||||
|
}
|
||||||
|
result.add(FieldInfo(inner.identifier.value, jsonName,
|
||||||
|
inner.declType.toTokens().toString(), inner.keyword.kind == TokenKind.VAR))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case _ => ()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 普通字段是否可序列化:_ 前缀 + 有类型注解
|
||||||
|
private func isSerializableField(vd: VarDecl): Bool {
|
||||||
|
vd.identifier.value.startsWith("_") && !vd.colon.value.isEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 从 MacroExpandDecl 提取内部 VarDecl(带注解的字段声明)
|
||||||
|
private func unwrapMacroField(md: MacroExpandDecl): ?VarDecl {
|
||||||
|
let visitor = FieldVisitor()
|
||||||
|
md.traverse(visitor)
|
||||||
|
visitor.getResult()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 遍历器:在 MacroExpandDecl 内部查找 VarDecl
|
||||||
|
private class FieldVisitor <: Visitor {
|
||||||
|
private var _decl: ?VarDecl = None
|
||||||
|
|
||||||
|
public override func visit(decl: VarDecl) {
|
||||||
|
_decl = Some(decl)
|
||||||
|
this.breakTraverse()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func getResult(): ?VarDecl {
|
||||||
|
_decl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 去掉字符串两端引号(JsonPropertyName 属性 tokens 形如 "user_alias")
|
||||||
|
private func stripQuotes(s: String): String {
|
||||||
|
if (s.size >= 2 && s[0..1] == "\"" && s[s.size - 1..] == "\"") {
|
||||||
|
return s[1..s.size - 1]
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 生成 exportJsonFields 源码
|
||||||
|
private func buildExportSource(className: String, fields: ArrayList<FieldInfo>): String {
|
||||||
|
var sb = StringBuilder()
|
||||||
|
sb.append(" public static func exportJsonFields(instance: ${className}): HashMap<String, Any> {\n")
|
||||||
|
sb.append(" let m = HashMap<String, Any>()\n")
|
||||||
|
for (f in fields) {
|
||||||
|
sb.append(" m[\"${f.jsonName}\"] = instance.${f.fieldName}\n")
|
||||||
|
}
|
||||||
|
sb.append(" return m\n")
|
||||||
|
sb.append(" }\n")
|
||||||
|
sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 生成 importJsonFields 源码
|
||||||
|
private func buildImportSource(className: String, fields: ArrayList<FieldInfo>): String {
|
||||||
|
var sb = StringBuilder()
|
||||||
|
sb.append(" public static func importJsonFields(instance: ${className}, json: HashMap<String, Any>): Unit {\n")
|
||||||
|
sb.append(" for ((k, v) in json) {\n")
|
||||||
|
for (f in fields) {
|
||||||
|
if (!f.isVar) {
|
||||||
|
continue // let 字段不导入
|
||||||
|
}
|
||||||
|
sb.append(" if (k == \"${f.jsonName}\") {\n")
|
||||||
|
sb.append(" if (let val: ${f.typeName} <- v) {\n")
|
||||||
|
sb.append(" instance.${f.fieldName} = val\n")
|
||||||
|
sb.append(" }\n")
|
||||||
|
sb.append(" }\n")
|
||||||
|
}
|
||||||
|
sb.append(" }\n")
|
||||||
|
sb.append(" }\n")
|
||||||
|
sb.toString()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user