如何将JSON :: Any映射到Crystal语言的自定义对象?

时间:2017-12-29 01:30:56

标签: crystal-lang

如何将解析后的JSON映射为JSON::Any类型到自定义对象?

就我而言,我正在研究聊天客户端。 Chat API可以使用以下JSON响应请求:

{"ok" => true,
 "result" =>
  [{"update_id" => 71058322,
    "message" =>
     {"message_id" => 20,
      "from" => "Benjamin",
      "text" => "hey"}}]}

在我的API客户端代码中的某处,我解析这些JSON以执行一些基本的运行状况检查并将结果传递给响应使用者。在使用者中,我遍历result数组并尝试将每个更新转换为适当的对象:

module Types
  class Update
    JSON.mapping({
      update_id: {type: Int32},
      message:   {type: Message},
    })
  end
end

module Types
  class Message
    JSON.mapping({
      message_id: Int32,
      date:       Int32,
      text:       String,
    })
  end
end

return unless response["ok"]
response["result"].each do |data|
  update = Types::Update.from_json(data)
end

不幸的是,最后一行会导致编译错误:

no overload matches 'JSON::Lexer.new' with type JSON::Any

显然,Object.from_json只能接受String个JSON,但不能接受解析的JSON。在我的情况下,dataJSON::Any对象。

脏修复Types::Update.from_json(data.to_json)有效,但看起来很荒谬。

将JSON对象映射到保留所有嵌套结构的自定义类型的正确方法是什么?

1 个答案:

答案 0 :(得分:7)

JSON.mappingJSON.parse并没有很好地协同工作。要解决您的问题,您可以创建另一个映射Types::Result并使用Object.from_json解析一个孔json,这对使用起来更方便:

module Types
  class Message
    JSON.mapping(
      message_id: Int32,
      text:       String
    )
  end

  class Update
    JSON.mapping(
      update_id: Int32,
      message:   Message
    )
  end

  class Result
    JSON.mapping(
      success: { key: "ok", type: Bool },
      updates: { key: "result", type: Array(Update) }
    )
  end
end

result = Types::Result.from_json string_json
result.success                    # => true
result.updates.first.message.text # => "hey"