В создаваемом мною осколке кристалла данные должны извлекаться из разных конечных точек 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)
Каков наилучший подход, чтобы избежать ненужного дублирования