refactor: 注解与宏改名——Serializer 前缀统一命名
- @JsonPropertyName → @SerializerPropertyName - @JsonIgnore → @SerializerIgnore - @JsonParent → @SerializerParent(宏文件 JsonParentMacro.cj → SerializerParentMacro.cj) - 同步更新:JsonAnnotations、ReflectionCache、宏内部注解判断、 测试、方案文档、serialization_test 独立项目 - 16 用例全绿
This commit is contained in:
+23
-23
@@ -136,7 +136,7 @@ BaseUser.deserializeObject(dms, data, options) // 反序列化:先回填父
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 宏 @JsonParent(标注在父类上) │
|
||||
│ 宏 @SerializerParent(标注在父类上) │
|
||||
│ → 编译期解析父类声明的 var 字段 │
|
||||
│ → 自动生成两个静态方法(静态类型访问,无反射检查): │
|
||||
│ exportJsonFields(instance: Parent) │
|
||||
@@ -171,7 +171,7 @@ soulsoft 的 API 带泛型约束 `where T <: ISerializable`,不标宏的类在
|
||||
### 3.1 宏定义
|
||||
|
||||
```cangjie
|
||||
// 文件:src/json/JsonParentMacro.cj
|
||||
// 文件:src/json/SerializerParentMacro.cj
|
||||
// 包:simapi_serialization.json(宏包需单独编译,见 3.3)
|
||||
|
||||
macro package simapi_serialization.json
|
||||
@@ -180,10 +180,10 @@ import std.ast.*
|
||||
import std.collection.*
|
||||
|
||||
/// 标注在父类上:自动生成父类字段的 JSON 导出/导入静态方法
|
||||
public macro JsonParent(input: Tokens): Tokens {
|
||||
public macro SerializerParent(input: Tokens): Tokens {
|
||||
// 1. 解析输入的 ClassDecl(非类声明 → 抛 MacroException)
|
||||
// 2. 校验 open:非 open class → 抛 MacroException(见 3.1.1)
|
||||
// 3. 收集 public var 字段(跳过 @JsonIgnore)
|
||||
// 3. 收集 public var 字段(跳过 @SerializerIgnore)
|
||||
// 4. 生成 exportJsonFields / importJsonFields 两个静态方法
|
||||
// 5. 拼回原类声明 + 新方法,返回 Tokens
|
||||
}
|
||||
@@ -191,7 +191,7 @@ public macro JsonParent(input: Tokens): Tokens {
|
||||
|
||||
#### 3.1.1 open 处理(两种模式,默认自动补 open)
|
||||
|
||||
`@JsonParent` 的语义是「暴露**父类**字段给子类继承链」——标注的类**必须可被继承**。Cangjie 类默认不可继承(非 open 类被继承会编译报错 `super class is not inheritable`),所以宏展开时处理 `open`:
|
||||
`@SerializerParent` 的语义是「暴露**父类**字段给子类继承链」——标注的类**必须可被继承**。Cangjie 类默认不可继承(非 open 类被继承会编译报错 `super class is not inheritable`),所以宏展开时处理 `open`:
|
||||
|
||||
```cangjie
|
||||
/// 遍历类声明的修饰符,检查是否含 open(参考 soulsoft Extensions.cj isOpen)
|
||||
@@ -216,13 +216,13 @@ if (!isOpenClass(decl)) {
|
||||
|
||||
- **已实测可行**:宏给 `class Base` 自动加 `open` 后,子类 `class Child <: Base` 编译通过、正常运行(`macro_open_probe` 验证);
|
||||
- 宏展开的 `open` 在语义检查**之前**生效,所以后续继承检查能看到;
|
||||
- 用户少写一个关键字,标注 `@JsonParent` 本身就表达了「我要被继承」的意图。
|
||||
- 用户少写一个关键字,标注 `@SerializerParent` 本身就表达了「我要被继承」的意图。
|
||||
|
||||
**模式二:严格校验(不自动补,非 open 报错)**
|
||||
|
||||
```cangjie
|
||||
if (!isOpenClass(decl)) {
|
||||
throw ASTException("@JsonParent 只能标注在 open class 上,"
|
||||
throw ASTException("@SerializerParent 只能标注在 open class 上,"
|
||||
+ "因为非 open 类不能被继承,不存在子类场景。")
|
||||
}
|
||||
```
|
||||
@@ -230,7 +230,7 @@ if (!isOpenClass(decl)) {
|
||||
- 抛 `ASTException`(宏内实际可用异常类,soulsoft 同款)→ 编译期直接报错,错误定位到标注处(`main.cj:5:1` 形式);
|
||||
- 顺带校验:标注在 **struct / interface / enum / func** 等非 class 声明上也抛错(`parseDecl` 结果 `as ClassDecl` 失败即报,已实测)。
|
||||
|
||||
**取舍**:自动补 open 更省事但「隐式改变类语义」;严格校验更显式但要求用户记得写 `open`。默认建议模式一(自动补),可在宏属性中提供开关,如 `@JsonParent[requireOpen: true]` 切到模式二。
|
||||
**取舍**:自动补 open 更省事但「隐式改变类语义」;严格校验更显式但要求用户记得写 `open`。默认建议模式一(自动补),可在宏属性中提供开关,如 `@SerializerParent[requireOpen: true]` 切到模式二。
|
||||
|
||||
- 参考先例:soulsoft `Extensions.cj:75-82` 遍历 `modifiers` 判 `TokenKind.OPEN`;`SerializationMacro.cj:27` 用 `throw ASTException(...)` 做宏内校验;`cd.modifiers.add(Modifier(Token(TokenKind.OPEN)))` 与 soulsoft `funcDecl.modifiers.add(Modifier(Token(TokenKind.PUBLIC)))` 同款操作。
|
||||
|
||||
@@ -239,11 +239,11 @@ if (!isOpenClass(decl)) {
|
||||
对如下父类:
|
||||
|
||||
```cangjie
|
||||
@JsonParent
|
||||
@SerializerParent
|
||||
open class BaseUser {
|
||||
public var _id: String = ""
|
||||
public var _createdAt: String = ""
|
||||
@JsonIgnore
|
||||
@SerializerIgnore
|
||||
public var _temp: String = ""
|
||||
}
|
||||
```
|
||||
@@ -254,7 +254,7 @@ open class BaseUser {
|
||||
open class BaseUser {
|
||||
public var _id: String = ""
|
||||
public var _createdAt: String = ""
|
||||
@JsonIgnore
|
||||
@SerializerIgnore
|
||||
public var _temp: String = ""
|
||||
|
||||
/// 导出:字段名 → 值(静态类型访问)
|
||||
@@ -262,7 +262,7 @@ open class BaseUser {
|
||||
var m = HashMap<String, Any>()
|
||||
m["_id"] = instance._id
|
||||
m["_createdAt"] = instance._createdAt
|
||||
// _temp 被 @JsonIgnore 跳过
|
||||
// _temp 被 @SerializerIgnore 跳过
|
||||
m
|
||||
}
|
||||
|
||||
@@ -318,8 +318,8 @@ Cangjie 宏需要两步编译(`cjc --compile-macro` 先编宏,再编使用
|
||||
| 细节 | 处理 |
|
||||
|---|---|
|
||||
| 字段类型 | 用 `TypeInfo.get(字段类型名)` 在运行时做类型转换(import 侧) |
|
||||
| `@JsonPropertyName` | export 键用注解名;import 匹配也用注解名 |
|
||||
| `@JsonIgnore` | 直接跳过,不生成导出/导入语句 |
|
||||
| `@SerializerPropertyName` | export 键用注解名;import 匹配也用注解名 |
|
||||
| `@SerializerIgnore` | 直接跳过,不生成导出/导入语句 |
|
||||
| `let`(不可变)字段 | 只导出,不导入(导入侧跳过) |
|
||||
| 无字段 | 生成空方法(返回空 map / 空操作),保证约定方法一定存在 |
|
||||
| 命名冲突 | 若用户已手写同名静态方法,宏应报错或跳过(设计取舍:报错更安全) |
|
||||
@@ -435,10 +435,10 @@ private static let _parentImportCache = HashMap<String, Bool>()
|
||||
|
||||
```cangjie
|
||||
import simapi_serialization.*
|
||||
import simapi_serialization.json.JsonParent // 引入宏
|
||||
import simapi_serialization.json.SerializerParent // 引入宏
|
||||
|
||||
// 父类标注宏
|
||||
@JsonParent
|
||||
@SerializerParent
|
||||
open class BaseUser {
|
||||
public var _id: String = ""
|
||||
public var _createdAt: String = ""
|
||||
@@ -474,7 +474,7 @@ main() {
|
||||
|
||||
| 用例 | 断言 |
|
||||
|---|---|
|
||||
| 父类字段序列化 | 输出包含 `_id`/`_createdAt`,且 `@JsonIgnore` 父类字段不输出 |
|
||||
| 父类字段序列化 | 输出包含 `_id`/`_createdAt`,且 `@SerializerIgnore` 父类字段不输出 |
|
||||
| 父类字段反序列化 | `Deserialize<User>` 后父类字段值正确 |
|
||||
| 多层继承 | GrandParent → Parent → Child 三层字段全部输出 |
|
||||
| 中间层无宏 | 三层链中 Parent 不标宏:`_gp` 仍导出,`_p` 不导出(逐层跳过) |
|
||||
@@ -482,11 +482,11 @@ main() {
|
||||
| 子类字段与父类字段重名 | 子类优先(输出一份,值取子类的) |
|
||||
| 父类嵌套对象字段 | 父类字段值是对象时正确递归 |
|
||||
| 父类字段 null | 导入时保持默认值 |
|
||||
| 与 @JsonPropertyName 组合 | 父类字段用注解名输出/匹配 |
|
||||
| 与 @SerializerPropertyName 组合 | 父类字段用注解名输出/匹配 |
|
||||
| 顶层序列化父类类型实例 | `Serialize(BaseUser 实例)` 直接可用 |
|
||||
| **宏自动补 open** | `@JsonParent class Base`(无 open)展开后子类可继承(编译通过) |
|
||||
| **宏严格模式报错** | `@JsonParent[requireOpen: true]` 标注非 open class 编译失败 |
|
||||
| **宏非 class 标注报错** | 对 struct/interface/enum 标注 `@JsonParent` 编译失败 |
|
||||
| **宏自动补 open** | `@SerializerParent class Base`(无 open)展开后子类可继承(编译通过) |
|
||||
| **宏严格模式报错** | `@SerializerParent[requireOpen: true]` 标注非 open class 编译失败 |
|
||||
| **宏非 class 标注报错** | 对 struct/interface/enum 标注 `@SerializerParent` 编译失败 |
|
||||
|
||||
---
|
||||
|
||||
@@ -508,7 +508,7 @@ main() {
|
||||
|
||||
| | 路线 A:入口不变 + 父类宏(本方案) | 路线 B:soulsoft 全宏风格 |
|
||||
|---|---|---|
|
||||
| 宏标在哪 | **仅父类**标 `@JsonParent` | **每个类**标 `@Serialization[superType: 父类]` |
|
||||
| 宏标在哪 | **仅父类**标 `@SerializerParent` | **每个类**标 `@Serialization[superType: 父类]` |
|
||||
| 继承串联 | 库运行时反射 `superClass` 链 + 静态方法调用 | 编译期 `super.serializeObject(...)` / `Parent.deserializeObject(...)` 调用链 |
|
||||
| `Serialize(obj)` / `Deserialize<T>(json)` | ✅ **完全不变**(无泛型约束,任意类) | 需保留反射回退,否则无宏类编译期拒绝 |
|
||||
| 无宏的类 | ✅ 照旧全反射(向后兼容) | 无法序列化(除非入口加反射回退) |
|
||||
@@ -535,7 +535,7 @@ main() {
|
||||
|
||||
## 9. 实施步骤(评审通过后)
|
||||
|
||||
1. 新建宏文件 `src/json/JsonParentMacro.cj`(或独立宏包),实现 `JsonParent` 宏;
|
||||
1. 新建宏文件 `src/json/SerializerParentMacro.cj`(或独立宏包),实现 `SerializerParent` 宏;
|
||||
2. 验证 cjpm 对宏包的编译流程(风险 #1),必要时调整项目结构;
|
||||
3. 改 `JsonWriter.writeObject`:superClass 链调用 `exportJsonFields`;
|
||||
4. 改 `JsonReader.readObject`:superClass 链调用 `importJsonFields`;
|
||||
|
||||
Reference in New Issue
Block a user