如何在Nim中将对象转换为JSON

14

我正在使用 Nim 开发一个小型 Web 服务,需要使用 JSON 响应请求。我使用 Jester 模块实现这个服务。我希望能够使用 Nim 的基础库中的 JSON 模块构造具有字段和值的某种对象,然后将其转换为 JSON 字符串。但是,如何做到呢?或者在 Nim 中构建 JSON 是否有更好的方法?

4个回答

29

marshal模块包含一个通用的对象到JSON序列化算法,可适用于任何类型(目前使用运行时类型内省)。

import marshal

type
  Person = object
    age: int
    name: string

var p = Person(age: 38, name: "Torbjørn")

echo($$p)

输出结果将是:

{"age": 38, "name": "Torbj\u00F8rn"}

虽然Grzegorz的回答正是我所期望的,但这个答案也非常有用,有趣,而且比使用json模块更简单。太棒了! - Torbjørn
1
在 Nim 中似乎每个人都会制作运算符? - PascalVKooten
6
这不是在Nim中序列化JSON的正确方法。marshal模块相当于Python中的pickle,它使用JSON作为序列化格式纯属巧合,不要使用它来将对象序列化为JSON。特别是现在因为json模块支持了几乎相同的功能,所以更不应该这样做:https://play.nim-lang.org/#ix=2uPe - dom96

11
在Nim中,你可以使用json模块创建JsonNode对象,这些对象是对象变体。它们可以通过像newJObject()这样的单独过程构建,然后填充fields序列。另一种更快的方法是使用%()过程,它接受一个元组序列,其中一个值是带有json字段的字符串,另一个值是单个的JsonNode对象。

以下是两种方式的示例:

import json

type
  Person = object ## Our generic person record.
    age: int ## The age of the person.
    name: string ## The name of the person.

proc `%`(p: Person): JsonNode =
  ## Quick wrapper around the generic JObject constructor.
  result = %[("age", %p.age), ("name", %p.name)]

proc myCustomJson(p: Person): JsonNode =
  ## Custom method where we replicate manual construction.
  result = newJObject()
  # Initialize empty sequence with expected field tuples.
  var s: seq[tuple[key: string, val: JsonNode]] = @[]
  # Add the integer field tuple to the sequence of values.
  s.add(("age", newJInt(p.age)))
  # Add the string field tuple to the sequence of values.
  s.add(("name", newJString(p.name)))
  result.fields = s

proc test() =
  # Tests making some jsons.
  var p: Person
  p.age = 24
  p.name = "Minah"
  echo(%p) # { "age": 24,  "name": "Minah"}
  p.age = 33
  p.name = "Sojin"
  echo(%p) # { "age": 33,  "name": "Sojin"}
  p.age = 40
  p.name = "Britney"
  echo p.myCustomJson # { "age": 40,  "name": "Britney"}

when isMainModule: test()

2
抱歉,那是一种可怕的生成JSON的方式,现在没有人会通过手动编写序列化函数来生成它。 - Alex Craft
请参见 https://forum.nim-lang.org/t/6707 上关于这个错误答案的讨论。 - Jim Balter

8

对于其他人在这个讨论中找到了基于 marshal 的答案。请改用以下方法:

import json

type
  Person = object
    age: int
    name: string

var p = Person(age: 38, name: "Torbjørn")
echo(%p)

请注意,您不应该使用 marshal 来完成此目的,它相当于 Python 中的 pickle 模块,可能会生成包含额外数据的 JSON,这可能不是您想要的。此外,现在它只是偶然生成 JSON,未来可能会选择不同的格式。

2
好的,我刚试了一下,确实可以运行 https://play.nim-lang.org/#ix=2uPX ...这很不错。 - Anentropic
@Anentropic echo 隐式地在其参数上调用 $(请参阅 echo 的文档)。$ 是 Nim 的“toString”函数。因此,只需为您的数据类型实现 $,即可打印其字符串表示形式。 返回一个 JsonNode,并且 JsonNode 已经实现了 $,因此 echo %p(或 echo $ %p)可以工作。非常简单明了。 “假设如果您的 Person 类型具有不是字符串或整数的属性,则需要进行更多的工作”-当然...json 模块的实现者已经完成了这项工作。(而 int 并不特殊-int$ 由 Nim 库提供。) - Jim Balter
啊,我没注意到 $ 也参与了。json模块提供了实现%的基本类型,这些类型很容易转换为json(数字、字符串、序列、哈希表和对象),但是对于任何其他类型,您必须提供自己的实现(正如我们所期望的那样),否则当您echo(%p)时会出现编译错误。 - Anentropic
@Anentropic "否则当你echo(%p)时会出现编译错误" -- 当你引用%p时,会出现编译错误,这与echo无关。echo %p实际上是stdout.writeln $(%p)。如果没有在作用域中定义一个接受p类型参数的%,那么当然会出错。再次强调,这一切都非常简单,并不特定于json%echo等。 - Jim Balter
1
@JimBalter 我们达成了一致。 - Anentropic
显示剩余2条评论

2

请执行以下步骤:

import json

var jsonResponse = %*
                   {"data": [{ "id": 35,
                               "type": "car",
                               "attributes": {"color":"red"} }]}

var body = ""

toUgly(body, jsonResponse)

echo body

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接