Files
serialization-cj/src/json/JsonWriter.cj
T
xrain ff1bd51f52 fix: isNoneValue 误判非 Option 值为 None(ignoreNull 时所有字段被跳过)
- 原实现 unwrapOptionValue(v).isNone() 把「非 Option」也返回 None → isNone()=true
- 改为直接检查 EnumTypeInfo.destruct 构造器名是否 None,非枚举返回 false
- 新增 None 字段测试(remark → null;ignoreNull=true 时省略 remark 保留其他)
2026-08-17 11:20:12 +08:00

215 lines
7.7 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
* Copyright (c) 2025 SimcuTeam. All rights reserved.
* JsonWriter:对象 → JsonValue(全反射)。
*
* 集合处理:仓颉泛型是不变的(Array<String> 不能 cast 为 Array<Any>),
* 因此通过反射调用 iterator()/hasNext()/next() 与 keys()/get() 迭代元素。
*/
package simapi_serialization.json
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 值是否为 NoneOption 枚举的 None 构造器;非 Option 值返回 false
private static func isNoneValue(v: Any): Bool {
if (let et: EnumTypeInfo <- TypeInfo.of(v)) {
let (ctor, _) = et.destruct(v)
return ctor.name == "None"
}
false
}
/// 若值为 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
}
}