2026-08-17 12:11:35 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* Copyright (c) 2025 SimcuTeam. All rights reserved.
|
|
|
|
|
|
* JsonSerializer 单元测试(cjpm test)。
|
2026-08-17 13:58:05 +08:00
|
|
|
|
* 覆盖:基础类型 / 对象字段输出 / 注解(SerializerPropertyName、SerializerIgnore)/ Option /
|
2026-08-17 12:11:35 +08:00
|
|
|
|
* 集合(Array、ArrayList、HashSet)/ HashMap / 枚举 / 命名策略 /
|
|
|
|
|
|
* 继承(平台限制)/ maxDepth / 宽松类型转换。
|
|
|
|
|
|
*
|
|
|
|
|
|
* 运行:cjpm test
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
package simapi_serialization.json
|
|
|
|
|
|
|
|
|
|
|
|
import std.collection.*
|
|
|
|
|
|
import std.unittest.*
|
|
|
|
|
|
import std.unittest.testmacro.*
|
2026-08-17 13:41:16 +08:00
|
|
|
|
import simapi_serialization.macros.*
|
2026-08-17 12:11:35 +08:00
|
|
|
|
|
|
|
|
|
|
enum Role {
|
|
|
|
|
|
| Admin
|
|
|
|
|
|
| User
|
|
|
|
|
|
| Guest
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 混合测试:_ 前缀字段 与 无下划线字段 同时存在
|
|
|
|
|
|
class Address {
|
|
|
|
|
|
public var _city: String = "" // 下划线字段 → 原样输出 _city
|
|
|
|
|
|
public var zip: String = "" // 无下划线 → 原样输出 zip
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-17 13:58:05 +08:00
|
|
|
|
// 继承测试:父类标注 @SerializerParent 宏 → 自动补 open + 生成导出/导入静态方法,
|
2026-08-17 13:41:16 +08:00
|
|
|
|
// 父类字段(_id/_createdAt)经宏的静态类型访问绕开反射声明类校验
|
2026-08-17 13:58:05 +08:00
|
|
|
|
@SerializerParent
|
2026-08-17 12:11:35 +08:00
|
|
|
|
open class BaseUser {
|
|
|
|
|
|
public var _id: String = ""
|
|
|
|
|
|
public var _createdAt: String = ""
|
2026-08-17 13:58:05 +08:00
|
|
|
|
@SerializerIgnore
|
2026-08-17 13:41:16 +08:00
|
|
|
|
public var _temp: String = ""
|
2026-08-17 13:58:05 +08:00
|
|
|
|
@SerializerPropertyName["parent_alias"]
|
2026-08-17 13:41:16 +08:00
|
|
|
|
public var _parentAlias: String = ""
|
2026-08-17 12:11:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class User <: BaseUser {
|
|
|
|
|
|
public var _name: String = "" // 下划线字段
|
|
|
|
|
|
public var _age: Int64 = 0 // 下划线字段
|
|
|
|
|
|
public var score: Float64 = 0.0 // 无下划线
|
|
|
|
|
|
public var active: Bool = true // 无下划线
|
|
|
|
|
|
public var _role: Role = Role.User // 下划线字段(枚举)
|
|
|
|
|
|
public var opt: ?String = None // 无下划线(Option=Some)
|
|
|
|
|
|
public var remark: ?String = None // 无下划线(Option=None → null)
|
|
|
|
|
|
public var _tags: Array<String> = [] // 下划线字段(集合)
|
|
|
|
|
|
public var addr: Address = Address() // 无下划线(嵌套对象)
|
2026-08-17 13:58:05 +08:00
|
|
|
|
@SerializerPropertyName["user_alias"]
|
2026-08-17 12:11:35 +08:00
|
|
|
|
public var _alias: String = "" // 下划线 + 注解覆盖
|
2026-08-17 13:58:05 +08:00
|
|
|
|
@SerializerIgnore
|
2026-08-17 12:11:35 +08:00
|
|
|
|
public var temp: String = "secret" // 无下划线 + 忽略
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-17 13:41:16 +08:00
|
|
|
|
// 多层继承:爷(标宏)→ 父(不标宏)→ 子
|
2026-08-17 13:58:05 +08:00
|
|
|
|
@SerializerParent
|
2026-08-17 13:41:16 +08:00
|
|
|
|
open class GrandParent {
|
|
|
|
|
|
public var _gp: String = ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
open class MidParent <: GrandParent {
|
|
|
|
|
|
public var _mp: String = ""
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class LeafChild <: MidParent {
|
|
|
|
|
|
public var _lc: String = ""
|
|
|
|
|
|
public init() {}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-17 12:11:35 +08:00
|
|
|
|
func makeUser(): User {
|
|
|
|
|
|
let u = User()
|
2026-08-17 13:41:16 +08:00
|
|
|
|
u._id = "u-1"
|
|
|
|
|
|
u._createdAt = "2025-01-01"
|
|
|
|
|
|
u._temp = "secret-parent"
|
|
|
|
|
|
u._parentAlias = "parent-xiaoming"
|
2026-08-17 12:11:35 +08:00
|
|
|
|
u._name = "alice"
|
|
|
|
|
|
u._age = 30
|
|
|
|
|
|
u.score = 88.5
|
|
|
|
|
|
u.active = true
|
|
|
|
|
|
u._role = Role.Admin
|
|
|
|
|
|
u.opt = Some("hello")
|
|
|
|
|
|
u._tags = ["a", "b", "c"]
|
|
|
|
|
|
u.addr._city = "beijing"
|
|
|
|
|
|
u.addr.zip = "100000"
|
|
|
|
|
|
u._alias = "xiaoming"
|
|
|
|
|
|
u.temp = "hidden"
|
|
|
|
|
|
u
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func roleStr(r: Role): String {
|
|
|
|
|
|
match (r) {
|
|
|
|
|
|
case Role.Admin => "Admin"
|
|
|
|
|
|
case Role.User => "User"
|
|
|
|
|
|
case Role.Guest => "Guest"
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func toStr(x: Any): String {
|
|
|
|
|
|
if (let s: String <- x) { return s }
|
|
|
|
|
|
if (let s: ToString <- x) { return s.toString() }
|
|
|
|
|
|
"?"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@Test
|
|
|
|
|
|
public class JsonSerializerTests {
|
|
|
|
|
|
/// 基础类型序列化
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func basicTypes() {
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(Int64(42)), "42")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(Int32(7)), "7")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(Int16(3)), "3")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(UInt32(9)), "9")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize("hi"), "\"hi\"")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(true), "true")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(false), "false")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(Float64(3.5)), "3.500000")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 对象序列化:下划线保留、注解生效、忽略生效、None→null、父类字段不输出
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func objectSerialize() {
|
|
|
|
|
|
let json = JsonSerializer.Serialize(makeUser())
|
|
|
|
|
|
// 下划线字段原样输出
|
|
|
|
|
|
@Expect(json.contains("\"_name\": \"alice\""), true)
|
|
|
|
|
|
@Expect(json.contains("\"_age\": 30"), true)
|
|
|
|
|
|
// 无下划线字段原样输出
|
|
|
|
|
|
@Expect(json.contains("\"score\""), true)
|
|
|
|
|
|
@Expect(json.contains("\"active\": true"), true)
|
2026-08-17 13:58:05 +08:00
|
|
|
|
// @SerializerPropertyName 覆盖字段名
|
2026-08-17 12:11:35 +08:00
|
|
|
|
@Expect(json.contains("\"user_alias\": \"xiaoming\""), true)
|
|
|
|
|
|
@Expect(json.contains("\"_alias\""), false)
|
2026-08-17 13:58:05 +08:00
|
|
|
|
// @SerializerIgnore 字段不输出
|
2026-08-17 12:11:35 +08:00
|
|
|
|
@Expect(json.contains("temp"), false)
|
|
|
|
|
|
@Expect(json.contains("\"secret\""), false)
|
|
|
|
|
|
// Option:Some→值,None→null
|
|
|
|
|
|
@Expect(json.contains("\"opt\": \"hello\""), true)
|
|
|
|
|
|
@Expect(json.contains("\"remark\": null"), true)
|
|
|
|
|
|
// 枚举 → 名字字符串
|
|
|
|
|
|
@Expect(json.contains("\"_role\": \"Admin\""), true)
|
|
|
|
|
|
// 嵌套对象
|
|
|
|
|
|
@Expect(json.contains("\"_city\": \"beijing\""), true)
|
|
|
|
|
|
@Expect(json.contains("\"zip\": \"100000\""), true)
|
2026-08-17 13:58:05 +08:00
|
|
|
|
// 父类字段(@SerializerParent 宏):_id/_createdAt 输出,@SerializerIgnore 父类字段不输出
|
2026-08-17 13:41:16 +08:00
|
|
|
|
@Expect(json.contains("\"_id\": \"u-1\""), true)
|
|
|
|
|
|
@Expect(json.contains("\"_createdAt\": \"2025-01-01\""), true)
|
2026-08-17 13:58:05 +08:00
|
|
|
|
// 父类 @SerializerPropertyName["parent_alias"] 生效
|
2026-08-17 13:41:16 +08:00
|
|
|
|
@Expect(json.contains("\"parent_alias\": \"parent-xiaoming\""), true)
|
|
|
|
|
|
@Expect(json.contains("_parentAlias"), false)
|
2026-08-17 13:58:05 +08:00
|
|
|
|
// 父类 @SerializerIgnore _temp 不输出
|
2026-08-17 13:41:16 +08:00
|
|
|
|
@Expect(json.contains("secret-parent"), false)
|
2026-08-17 12:11:35 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 对象反序列化往返
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func objectRoundTrip() {
|
|
|
|
|
|
let json = JsonSerializer.Serialize(makeUser())
|
|
|
|
|
|
let u2 = JsonSerializer.Deserialize<User>(json)
|
|
|
|
|
|
@Expect(u2._name, "alice")
|
|
|
|
|
|
@Expect(u2._age, Int64(30))
|
|
|
|
|
|
@Expect(u2.active, true)
|
|
|
|
|
|
@Expect(roleStr(u2._role), "Admin")
|
|
|
|
|
|
@Expect(u2.addr._city, "beijing")
|
|
|
|
|
|
@Expect(u2.addr.zip, "100000")
|
|
|
|
|
|
@Expect(u2._alias, "xiaoming")
|
2026-08-17 13:58:05 +08:00
|
|
|
|
@Expect(u2.temp, "secret") // @SerializerIgnore:反序列化不改动默认值
|
|
|
|
|
|
// 父类字段往返(@SerializerParent 宏)
|
2026-08-17 13:41:16 +08:00
|
|
|
|
@Expect(u2._id, "u-1")
|
|
|
|
|
|
@Expect(u2._createdAt, "2025-01-01")
|
|
|
|
|
|
@Expect(u2._parentAlias, "parent-xiaoming")
|
2026-08-17 12:11:35 +08:00
|
|
|
|
// Option 往返
|
|
|
|
|
|
let optVal: ?String = u2.opt
|
|
|
|
|
|
@Expect(optVal.isSome(), true)
|
|
|
|
|
|
if (let Some(v) <- optVal) {
|
|
|
|
|
|
@Expect(v, "hello")
|
|
|
|
|
|
}
|
|
|
|
|
|
let remarkVal: ?String = u2.remark
|
|
|
|
|
|
@Expect(remarkVal.isNone(), true)
|
|
|
|
|
|
// 集合往返
|
|
|
|
|
|
@Expect(u2._tags.size, Int64(3))
|
|
|
|
|
|
@Expect(u2._tags[0], "a")
|
|
|
|
|
|
@Expect(u2._tags[1], "b")
|
|
|
|
|
|
@Expect(u2._tags[2], "c")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Array 往返
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func arrayRoundTrip() {
|
|
|
|
|
|
let json = JsonSerializer.Serialize(["x", "y", "z"])
|
|
|
|
|
|
let arrBack = JsonSerializer.Deserialize<Array<String>>(json)
|
|
|
|
|
|
@Expect(arrBack.size, Int64(3))
|
|
|
|
|
|
@Expect(arrBack[0], "x")
|
|
|
|
|
|
@Expect(arrBack[2], "z")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// ArrayList 往返
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func arrayListRoundTrip() {
|
|
|
|
|
|
var list = ArrayList<String>()
|
|
|
|
|
|
list.add("a")
|
|
|
|
|
|
list.add("b")
|
|
|
|
|
|
let json = JsonSerializer.Serialize(list)
|
|
|
|
|
|
let back = JsonSerializer.Deserialize<ArrayList<String>>(json)
|
|
|
|
|
|
@Expect(back.size, Int64(2))
|
|
|
|
|
|
@Expect(back[0], "a")
|
|
|
|
|
|
@Expect(back[1], "b")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// HashSet 往返
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func hashSetRoundTrip() {
|
|
|
|
|
|
var set = HashSet<String>()
|
|
|
|
|
|
set.add("x")
|
|
|
|
|
|
set.add("y")
|
|
|
|
|
|
let json = JsonSerializer.Serialize(set)
|
|
|
|
|
|
let back = JsonSerializer.Deserialize<HashSet<String>>(json)
|
|
|
|
|
|
@Expect(back.contains("x"), true)
|
|
|
|
|
|
@Expect(back.contains("y"), true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// HashMap<String, V> 往返
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func hashMapRoundTrip() {
|
|
|
|
|
|
let m = HashMap<String, Any>()
|
|
|
|
|
|
m["k1"] = "v1"
|
|
|
|
|
|
m["k2"] = Int64(42)
|
|
|
|
|
|
let json = JsonSerializer.Serialize(m)
|
|
|
|
|
|
let mBack = JsonSerializer.Deserialize<HashMap<String, Any>>(json)
|
|
|
|
|
|
@Expect(toStr(mBack["k1"]), "v1")
|
|
|
|
|
|
@Expect(toStr(mBack["k2"]), "42")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 命名策略:NONE / CAMEL / SNAKE 对当前模型(全小写+下划线)输出一致
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func namingPolicies() {
|
|
|
|
|
|
let optNone = JsonOption()
|
|
|
|
|
|
optNone.propertyNamingPolicy = PropertyNamingPolicy.None
|
|
|
|
|
|
let jsonNone = JsonSerializer.Serialize(makeUser(), optNone)
|
|
|
|
|
|
|
|
|
|
|
|
let optCamel = JsonOption()
|
|
|
|
|
|
optCamel.propertyNamingPolicy = PropertyNamingPolicy.CamelCase
|
|
|
|
|
|
let jsonCamel = JsonSerializer.Serialize(makeUser(), optCamel)
|
|
|
|
|
|
|
|
|
|
|
|
let optSnake = JsonOption()
|
|
|
|
|
|
optSnake.propertyNamingPolicy = PropertyNamingPolicy.SnakeCase
|
|
|
|
|
|
let jsonSnake = JsonSerializer.Serialize(makeUser(), optSnake)
|
|
|
|
|
|
|
|
|
|
|
|
// 模型字段全为小写/下划线,三种策略结果应一致
|
|
|
|
|
|
@Expect(jsonNone, jsonCamel)
|
|
|
|
|
|
@Expect(jsonNone, jsonSnake)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 命名策略转换规则(直接单测)
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func applyNamingPolicy() {
|
|
|
|
|
|
// SnakeCase:大写转 _小写
|
|
|
|
|
|
@Expect(ReflectionCache.applyNamingPolicy("userName", PropertyNamingPolicy.SnakeCase), "user_name")
|
|
|
|
|
|
@Expect(ReflectionCache.applyNamingPolicy("userNameAge", PropertyNamingPolicy.SnakeCase), "user_name_age")
|
|
|
|
|
|
// 下划线不剥除、不双写
|
|
|
|
|
|
@Expect(ReflectionCache.applyNamingPolicy("_name", PropertyNamingPolicy.SnakeCase), "_name")
|
|
|
|
|
|
// CamelCase:仅首字母小写
|
|
|
|
|
|
@Expect(ReflectionCache.applyNamingPolicy("Name", PropertyNamingPolicy.CamelCase), "name")
|
|
|
|
|
|
@Expect(ReflectionCache.applyNamingPolicy("_name", PropertyNamingPolicy.CamelCase), "_name")
|
|
|
|
|
|
// None:原样
|
|
|
|
|
|
@Expect(ReflectionCache.applyNamingPolicy("Name", PropertyNamingPolicy.None), "Name")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 枚举序列化/反序列化
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func enumSerialize() {
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(Role.Admin), "\"Admin\"")
|
|
|
|
|
|
@Expect(JsonSerializer.Serialize(Role.Guest), "\"Guest\"")
|
|
|
|
|
|
let u = User()
|
|
|
|
|
|
u._role = Role.Guest
|
|
|
|
|
|
let back = JsonSerializer.Deserialize<User>(JsonSerializer.Serialize(u))
|
|
|
|
|
|
@Expect(roleStr(back._role), "Guest")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Option 字段:null → None,值 → Some
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func optionField() {
|
|
|
|
|
|
let none: ?String = JsonSerializer.Deserialize<?String>("null")
|
|
|
|
|
|
@Expect(none.isNone(), true)
|
|
|
|
|
|
let some: ?String = JsonSerializer.Deserialize<?String>("\"hi\"")
|
|
|
|
|
|
@Expect(some.isSome(), true)
|
|
|
|
|
|
if (let Some(v) <- some) {
|
|
|
|
|
|
@Expect(v, "hi")
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 宽松类型转换:JSON 数字 ↔ 字符串字段互转
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func looseConversion() {
|
|
|
|
|
|
// 数字 → String 字段
|
|
|
|
|
|
let s: String = JsonSerializer.Deserialize<String>("42")
|
|
|
|
|
|
@Expect(s, "42")
|
|
|
|
|
|
// 字符串 → Int64 字段
|
|
|
|
|
|
let i: Int64 = JsonSerializer.Deserialize<Int64>("\"42\"")
|
|
|
|
|
|
@Expect(i, Int64(42))
|
|
|
|
|
|
// 浮点 → Int64(截断)
|
|
|
|
|
|
let j: Int64 = JsonSerializer.Deserialize<Int64>("3.9")
|
|
|
|
|
|
@Expect(j, Int64(3))
|
|
|
|
|
|
// 字符串 → Bool
|
|
|
|
|
|
let b: Bool = JsonSerializer.Deserialize<Bool>("\"true\"")
|
|
|
|
|
|
@Expect(b, true)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 深度限制:超过 maxDepth 抛异常
|
|
|
|
|
|
@TestCase
|
|
|
|
|
|
public func maxDepth() {
|
|
|
|
|
|
let opt = JsonOption()
|
|
|
|
|
|
opt.maxDepth = 0
|
|
|
|
|
|
let u = User()
|
|
|
|
|
|
let threw = try {
|
|
|
|
|
|
JsonSerializer.Serialize(u, opt)
|
|
|
|
|
|
false
|
|
|
|
|
|
} catch (_: Exception) {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
|
|
|
|
|
@Expect(threw, true)
|
|
|
|
|
|
}
|
2026-08-17 13:41:16 +08:00
|
|
|
|
|
2026-08-17 13:58:05 +08:00
|
|
|
|
/// 父类字段序列化/反序列化(@SerializerParent 宏):已并入 objectSerialize/objectRoundTrip,
|
2026-08-17 13:41:16 +08:00
|
|
|
|
/// 此处验证宏生成的静态方法可直接调用
|
|
|
|
|
|
@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")
|
2026-08-17 13:58:05 +08:00
|
|
|
|
// @SerializerIgnore 父类字段不导出
|
2026-08-17 13:41:16 +08:00
|
|
|
|
@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, "") // 中间层字段保持默认值
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-08-17 13:58:05 +08:00
|
|
|
|
/// 宏自动补 open:GrandParent 未显式写 open(@SerializerParent 自动补),继承链可正常编译
|
2026-08-17 13:41:16 +08:00
|
|
|
|
@TestCase
|
|
|
|
|
|
public func parentAutoOpen() {
|
|
|
|
|
|
let leaf = LeafChild()
|
|
|
|
|
|
leaf._gp = "auto"
|
|
|
|
|
|
let json = JsonSerializer.Serialize(leaf)
|
|
|
|
|
|
@Expect(json.contains("\"_gp\": \"auto\""), true)
|
|
|
|
|
|
}
|
2026-08-17 12:11:35 +08:00
|
|
|
|
}
|