如何使用 marshal 序列化 Nim 对象

220 阅读2分钟

Nim编程早茶

这一节我们介绍如何使用标准库 marshal 序列化 Nim 对象。适用于 Nim 1.0.x 系列。

简单的例子

我们可以使用 marshal 序列化 Nim 对象, nowtimes 模块中用来获取当前时刻的函数。

我们使用 $$ 来序列化 Nim 对象,目前 marshal 库使用 json 来序列化,也就是说,Nim 对象会被转换为 json 对象。

我们可以使用 to 宏来从序列化内容中,恢复 Nim 对象。其中 DateTime 是反序列化得到的对象类型。

import times, marshal


let t = now()
echo t
# 序列化
let t_marshal = $$t
echo t_marshal
# 反序列化
let t_unmarshal = to[DateTime](t_marshal)
assert t == t_unmarsha

输出

2019-11-24T20:06:32+08:00
{"nanosecond": 830475600, "second": 32, "minute": 6, "hour": 20, "monthday": 24, "month": "November", "year": 2019, "weekday": "Sunday", "yearday": 327, "isDst": false, "timezone": [1503304, {"zonedTimeFromTimeImpl": {"Field0": 4321957, "Field1": null}, "zonedTimeFromAdjTimeImpl": {"Field0": 4322426, "Field1": null}, "name": "LOCAL"}], "utcOffset": -28800}

编译时执行

其中 $$ 和 to 都是可以在编译期执行的。我们可以使用 static 宏来测试。

import times, marshal

type
  Animal = object of RootObj
    name: string
    birthday: DateTime
    num: int

static:
  let t = Animal(name: "animal", num: 12, birthday: now())
  echo t
  # 序列化
  let t_marshal = $$t
  echo t_marshal
  # 反序列化
  let t_unmarshal = to[Animal](t_marshal)
  echo t_unmarshal

输出

(name: "animal", birthday: 2019-11-25T09:41:41+08:00, num: 12)
{"name": "animal", "birthday": {"nanosecond": 735557600, "second": 41, "minute": 41, "hour": 9, "monthday": 25, "month": "November", "year": 2019, "weekday": "Monday", "yearday": 328, "isDst": false, "timezone": [6881352, {"zonedTimeFromTimeImpl": {"Field0": 4325685, "Field1": null}, "zonedTimeFromAdjTimeImpl": {"Field0": 4326154, "Field1": null}, "name": "LOCAL"}], "utcOffset": -28800}, "num": 12}
(name: "animal", birthday: 2019-11-25T09:41:41+08:00, num: 12)

marshal 库的限制

要注意,marshal 模块中的序列化,并没有序列化对象的类型。如果对象使用了运行时类型,就会出现错误。

Cat 继承 Animal 类型,但编译时,并不能获得这种继承关系,所以说,to[Cat](t_marshal) 并不能识别 Animalnamenum 属性。

type
  Animal = object of RootObj
    name: string
    num: int
  Wolf = object of Animal
    bark: bool
  Cat = object of Animal
    bark: bool
  Dog = object
    name: string
    num: int
    bark: bool

static:
  let t = Cat(name: "animal", num: 12)
  echo t
  # 序列化
  let t_marshal = $$t
  echo t_marshal
  # 反序列化
  let t_unmarshal = to[Cat](t_marshal)
  echo t_unmarshal

错误信息:

Error: unhandled exception: unknown file(1, 7) Error: unknown field for object of type Cat expected [JsonParsingError]

从文件中加载对象

借助 marshal 模块中的 loadstore 函数,我们可以对 stringStreamfileStream 序列化与反序列化。

import marshal, streams

# 从 stringStream 加载 array 对象
var s0 = newStringStream("[1, 2, 3]")
var a0: array[3, int]
load(s0, a0)


let t = now()
var s1 = newStringStream("")
# 序列化
let t_marshal = $$t
# 存储序列化对象
store(s1, t_marshal)
# 反序列化
let t_unmarshal = to[DateTime](t_marshal)
assert t == t_unmarshal
# 文件中读取
# s1.setPosition(0)
# echo s1.readAll
echo t_marshal

输出

{"nanosecond": 237341300, "second": 44, "minute": 27, "hour": 20, "monthday": 24, "month": "November", "year": 2019, "weekday": "Sunday", "yearday": 327, "isDst": false, "timezone": [1531976, {"zonedTimeFromTimeImpl": {"Field0": 4321957, "Field1": null}, "zonedTimeFromAdjTimeImpl": {"Field0": 4322426, "Field1": null}, "name": "LOCAL"}], "utcOffset": -28800}