Разбор потока JSON с помощью Argonaut

Я использую Argonaut для анализа объектов от удаленного поставщика JSON. API имеет два типа конечных точек: традиционный запрос REST по URL-адресу и ответ одного объекта JSON. Я могу легко анализировать сложные возвращаемые объекты JSON с помощью Argonaut на этом типе конечной точки.

Моя проблема связана с конечной точкой потоковой передачи поставщика, которая возвращает случайные объекты JSON из ограниченного набора JSON для данной конечной точки. Объекты возвращаются в том порядке, в котором они встречаются на сайте, и любой из примерно двадцати различных объектов может быть возвращен в любое время.

Работая через API, я не могу найти способ решить эту проблему с помощью Argonaut. Кажется, что все API-интерфейсы требуют параметризации типов, что сложно в среде, где невозможно предсказать тип следующего объекта. Одним из вариантов является отправка в разные кодеки на основе первых нескольких символов в каждом блоке JSON, но это подрывает цель отправки строки JSON парсеру и получения объекта взамен.

Лучшее, что мне удалось найти до сих пор, это чтобы все классы case верхнего уровня расширяли пустой trait:

implicit def ModelDecodeJson: DecodeJson[Model] =
  DecodeJson(c =>
    c.as[ModelSubclassA].asInstanceOf[DecodeResult[Model]]
      ||| c.as[ModelSubclassB].asInstanceOf[DecodeResult[Model]]
      // many more here!
  )

К сожалению, ModelSubclassA и ModelSubclassB имеют несколько ассоциаций с другими классами case, и, хотя этот пример компилируется, во время выполнения происходит сбой при попытке анализа этих подтипов. Всего будет несколько десятков case-классов, формирующих иерархию возвращаемых данных.

Я также пытался построить это с пониманием for, но и здесь не повезло.

Может ли кто-нибудь посоветовать лучшие образцы здесь?

ОБНОВЛЕНИЕ

Следующее выглядит более масштабируемым, но типы не взаимодействуют друг с другом:

implicit def ModelDecodeJson: DecodeJson[Model] =
  DecodeJson(c =>
    (c.as[ModelSubclassA] ||| c.as[ModelSubclassB]).asInstanceOf[DecodeResult[Model]]
  )

Ошибка:(10, 17) несоответствие типов; найдено: argonaut.DecodeResult[ModelSubclassB] требуется: argonaut.DecodeResult[Product with Serializable with Model] Примечание: ModelSubclassB ‹: продукт с Serializable with Model, но класс DecodeResult является инвариантным в типе A. Вместо этого вы можете определить A как +A . (SLS 4.5) ||| c.as[ModelSubclassB]).asInstanceOf[DecodeResult[Model]] ^

Итак, я начал смотреть исходный код и понял, что определение DecodeResult было изменено, чтобы включить +A, как было предложено ошибкой в ​​​​версии 6.2-M1. Обновление до этой версии, к сожалению, превратило все кодеки подкласса Model в неоднозначные имплициты, что имеет смысл.

Фу...


person Brian Topping    schedule 20.02.2016    source источник


Ответы (1)


Ответ на это требует двух частей:

  1. «Типы суммы» инкапсулируют значения и отделяют кодеки от типов, используемых для возвращаемых значений. В приведенном выше примере трейт Model используется кодеками для разрешения имплицитов. Если он также используется в качестве возвращаемого типа, вводятся рекурсивные определения, которые компилятор не может однозначно разрешить.

  2. Как только используются типы сумм, клиенту легко принять эти типы и использовать экстрактор в match, чтобы получить реальное значение внутри.

person Brian Topping    schedule 24.02.2016