Правильное перемещение типов с помощью JSON :: PullParser - PullRequest
0 голосов
/ 25 февраля 2020

В создаваемом мною осколке кристалла данные должны извлекаться из разных конечных точек 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)

Каков наилучший подход, чтобы избежать ненужного дублирования

1 Ответ

0 голосов
/ 25 февраля 2020

После оживленной прогулки я нашел решение, которое работает. С помощью макроса можно захватить 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

Не уверен, что это правильный / лучший подход, но он работает как положено.

...