【发布时间】:2020-02-25 13:29:21
【问题描述】:
在我创建的 Crystal 分片中,需要从不同的 API 端点提取数据。集合端点将响应如下数组:
json = %({
"_embedded": [
{"id":"item_1"},
{"id":"item_2"}
]
})
为了解释数组并将其转换为对象数组,我准备了以下转换器:
struct ListConverter(T)
def self.from_json(pull : JSON::PullParser)
items = Array(T).new
pull.read_array do
items.push(Item.from_json(pull.read_raw))
end
items
end
end
有两个抽象结构。一个用于数组中的项目,另一个用于列表本身,其中包括Enumerable:
abstract struct Base
include JSON::Serializable
end
abstract struct List(T) < Base
include Enumerable(T)
@[JSON::Field(key: "_embedded", converter: ListConverter(Item))]
getter items : Array(T)
def each(&block : T -> _)
end
end
最后是实现:
struct Item < Base
getter id : String?
end
struct ItemList < List(Item)
end
list = ItemList.from_json(json)
这一切都很好,除了一件事。列表转换器需要传递确切的项目类型:
@[JSON::Field(key: "_embedded", converter: ListConverter(Item))]
我希望能够做到这一点,但当然,这不起作用,因为T 没有在运行时定义(我认为):
@[JSON::Field(key: "_embedded", converter: ListConverter(T))]
所以现在我必须在每个继承自 List 的结构中定义以下行:
@[JSON::Field(key: "_embedded", converter: ListConverter(Item))]
getter items : Array(T)
避免不必要的重复的最佳方法是什么?
【问题讨论】:
-
我不确定您的实际用例是否还有其他问题。但是在您在此处发布的示例中,您根本不需要自定义
ListConverter。Array已经涵盖了这一点。只需省略converter:配置就可以完美运行。 -
谢谢,不知道它可以按原样转换数组。很高兴知道。虽然,在我的场景中,数组嵌套在一个对象内。像这样的东西:
{ "_embedded": { "items": [{"id":"item_1"},{"id":"item_2"}] } }。并且密钥(items)根据T的名称而有所不同。 -
这也应该有效。
JSON::Field上有一个root属性用于此目的。例如,请参阅carc.in/#/r/8mhf。 -
更好!现在我不再需要转换器了。再次感谢! github.com/wout/mollie.cr/blob/master/src/mollie/list.cr#L3-L9
标签: json crystal-lang