使用 JSON::PullParser 正确移动类型
Correctly moving around types with JSON::PullParser
在我正在创建的 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)
避免不必要重复的最佳方法是什么?
快步走后,我想出了一个可行的解决方案。通过使用宏,可以捕获 T
并且可以使用宏将转换器传递给 JSON::Field
注解:
abstract struct List(T) < Base
include Enumerable(T)
macro list_converter
ListConverter({{ T.id }})
end
@[JSON::Field(key: "_embedded", converter: list_converter)]
getter items : Array(T)
def each(&block : T -> _)
end
end
不确定这是否是 right/best 方法,但它按预期工作。
在我正在创建的 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)
避免不必要重复的最佳方法是什么?
快步走后,我想出了一个可行的解决方案。通过使用宏,可以捕获 T
并且可以使用宏将转换器传递给 JSON::Field
注解:
abstract struct List(T) < Base
include Enumerable(T)
macro list_converter
ListConverter({{ T.id }})
end
@[JSON::Field(key: "_embedded", converter: list_converter)]
getter items : Array(T)
def each(&block : T -> _)
end
end
不确定这是否是 right/best 方法,但它按预期工作。