feat: JsonWriter/JsonReader 支持 DateTime 字段(序列化为 yyyy-MM-dd HH:mm:ss 字符串,可反序列化回,含 Option<DateTime>)

This commit is contained in:
2026-08-21 04:09:42 +08:00
parent ab1e04787e
commit 46bd710128
4 changed files with 55 additions and 1 deletions
+4 -1
View File
@@ -1,2 +1,5 @@
target/
*.cj.macrocall
*.cj.macrocall
# 宏编译产物(构建时自动生成,不入库)
src/macros/*.cjo
src/macros/*.dll
+12
View File
@@ -12,6 +12,7 @@ package simcu::serialization.json
import std.collection.*
import std.convert.*
import std.reflect.*
import std.time.*
import stdx.encoding.json.*
import simcu::serialization.common.ReflectionCache
import simcu::serialization.common.FieldMetadata
@@ -62,6 +63,7 @@ public class JsonReader {
case "Float64" => return readFloat64(value)
case "Float32" => return Float32(readFloat64(value))
case "Rune" => return readChar(value)
case "DateTime" | "std.time.DateTime" => return readDateTime(value)
case _ => ()
}
@@ -298,6 +300,15 @@ public class JsonReader {
throw Exception("JsonSerializer: 无法转换为 Char")
}
private static func readDateTime(v: JsonValue): DateTime {
let s = readString(v)
try {
return DateTime.parse(s, "yyyy-MM-dd HH:mm:ss")
} catch (_: Exception) {
throw Exception("JsonSerializer: 无法转换为 DateTime: ${s}")
}
}
/// 生成某类型的占位默认值(用于构造 Option<X> 的 NoneNone 的参数槽类型是 X)。
private static func defaultValueFor(typeName: String): Any {
match (typeName) {
@@ -314,6 +325,7 @@ public class JsonReader {
case "Float64" => return Float64(0.0)
case "Float32" => return Float32(0.0)
case "Rune" => return Rune(0)
case "DateTime" | "std.time.DateTime" => return DateTime.parse("1970-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss")
case _ => ()
}
// 集合/对象/枚举:反射无参构造;失败则抛异常
+2
View File
@@ -10,6 +10,7 @@ package simcu::serialization.json
import std.collection.*
import std.reflect.*
import std.time.*
import stdx.encoding.json.*
import simcu::serialization.common.ReflectionCache
import simcu::serialization.common.FieldMetadata
@@ -42,6 +43,7 @@ public class JsonWriter {
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}") }
if (let dt: DateTime <- value) { return JsonString(dt.format("yyyy-MM-dd HH:mm:ss")) }
let typeName = TypeInfo.of(value).toString()
+37
View File
@@ -11,6 +11,7 @@
package simcu::serialization.tests
import std.collection.*
import std.time.*
import std.unittest.*
import std.unittest.testmacro.*
import simcu::serialization.*
@@ -55,6 +56,13 @@ class User <: BaseUser {
public var temp: String = "secret" // 无下划线 + 忽略
}
// DateTime 字段测试实体
class Event {
public var _name: String = ""
public var _time: DateTime = DateTime.parse("1970-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss")
public var _optTime: ?DateTime = None
}
// 多层继承:爷(标宏)→ 父(不标宏)→ 子
@SerializerParent
open class GrandParent {
@@ -304,6 +312,35 @@ public class JsonSerializerTests {
}
}
/// DateTime:输出 yyyy-MM-dd HH:mm:ss 字符串并可反序列化回
@TestCase
public func dateTimeRoundTrip(): Unit {
let e = Event()
e._name = "login"
e._time = DateTime.parse("2026-08-21 02:27:48", "yyyy-MM-dd HH:mm:ss")
e._optTime = Some(DateTime.parse("2026-01-02 03:04:05", "yyyy-MM-dd HH:mm:ss"))
let json = JsonSerializer.Serialize(e)
@Expect(json.contains("\"_time\": \"2026-08-21 02:27:48\""), true)
@Expect(json.contains("\"_optTime\": \"2026-01-02 03:04:05\""), true)
let back = JsonSerializer.Deserialize<Event>(json)
@Expect(back._time.format("yyyy-MM-dd HH:mm:ss"), "2026-08-21 02:27:48")
let optVal: ?DateTime = back._optTime
@Expect(optVal.isSome(), true)
if (let Some(dt) <- optVal) {
@Expect(dt.format("yyyy-MM-dd HH:mm:ss"), "2026-01-02 03:04:05")
}
// 顶层 DateTime 直接序列化
@Expect(JsonSerializer.Serialize(back._time), "\"2026-08-21 02:27:48\"")
// Option<DateTime> None → null
let e2 = Event()
e2._name = "n"
e2._time = DateTime.parse("2026-01-01 00:00:00", "yyyy-MM-dd HH:mm:ss")
let json2 = JsonSerializer.Serialize(e2)
@Expect(json2.contains("\"_optTime\": null"), true)
let back2 = JsonSerializer.Deserialize<Event>(json2)
@Expect(back2._optTime.isNone(), true)
}
/// 宽松类型转换:JSON 数字 ↔ 字符串字段互转
@TestCase
public func looseConversion(): Unit {