После ответа Идана Вайсмана и ответа C4stor в моем дубликате question Я использовал шаблон Type Classes. Для краткости я привожу пример кода только для декодирования json. Точно так же можно реализовать кодирование.
Во-первых, давайте определим трейт, который будет использоваться для внедрения зависимости декодера json:
trait JsonDecoder[T] {
def apply(s: String): Option[T]
}
Затем мы определяем объект, который создает экземпляр, реализующий эту черту:
import io.circe.Decoder
import io.circe.parser.decode
object CirceDecoderProvider {
def apply[T: Decoder]: JsonDecoder[T] =
new JsonDecoder[T] {
def apply(s: String) =
decode[T](s).fold(_ => None, s => Some(s))
}
}
Как вы можете заметить, apply
требует, чтобы неявный io.circe.Decoder[T]
находился в области видимости при вызове.
Затем мы копируем io.circe.generic.auto
содержимое объекта и создаем черту (я сделал PR, чтобы эта черта доступно как io.circe.generic.Auto
):
import io.circe.export.Exported
import io.circe.generic.decoding.DerivedDecoder
import io.circe.generic.encoding.DerivedObjectEncoder
import io.circe.{ Decoder, ObjectEncoder }
import io.circe.generic.util.macros.ExportMacros
import scala.language.experimental.macros
trait Auto {
implicit def exportDecoder[A]: Exported[Decoder[A]] = macro ExportMacros.exportDecoder[DerivedDecoder, A]
implicit def exportEncoder[A]: Exported[ObjectEncoder[A]] = macro ExportMacros.exportEncoder[DerivedObjectEncoder, A]
}
Затем в пакете (например, com.example.app.json
), который часто использует декодирование json, мы создаем объект пакета, если он не существует, и заставляем его расширять черту Auto
и обеспечивать неявный возврат JsonDecoder[T]
для данного типа T
:
package com.example.app
import io.circe.Decoder
package object json extends Auto {
implicit def decoder[T: Decoder]: JsonDecoder[T] = CirceDecoderProvider[T]
}
Теперь:
- все исходные файлы в
com.example.app.json
имеют Auto
значение
- вы можете получить
JsonDecoder[T]
для любого типа T
, который имеет io.circe.Decoder[T]
или для которого он может быть сгенерирован с Auto
имплицитами
- вам не нужно импортировать
io.circe.generic.auto._
в каждый файл
- вы можете переключаться между json-библиотеками, изменяя только
com.example.app.json
содержимое объекта пакета.
Например, вы можете переключиться на json4s (хотя я сделал наоборот и переключился на circe с json4s). Провайдер внедрения для JsonDecoder[T]
:
import org.json4s.Formats
import org.json4s.native.JsonMethods._
import scala.util.Try
case class Json4SDecoderProvider(formats: Formats) {
def apply[T: Manifest]: JsonDecoder[T] =
new JsonDecoder[T] {
def apply(s: String) = {
implicit val f = formats
Try(parse(s).extract[T]).toOption
}
}
}
И измените содержимое объекта пакета com.example.app.json
на:
package com.example.app
import org.json4s.DefaultFormats
package object json {
implicit def decoder[T: Manifest]: JsonDecoder[T] = Json4SDecoderProvider(DefaultFormats)[T]
}
С помощью шаблона Type Classes вы получаете инъекцию зависимостей во время компиляции. Это дает вам меньшую гибкость, чем внедрение зависимостей во время выполнения, но я сомневаюсь, что вам нужно переключать парсеры json во время выполнения.
person
mixel
schedule
12.12.2016