5

我正在尝试使用 scala json 库 Circe,将其包装在一个简单的特征中以提供与 json 的转换,我有以下内容:

import io.circe.generic.auto._
import io.circe.parser._
import io.circe.syntax._

trait JsonConverter {
  def toJson[T](t : T) : String
  def fromJson[T](s: String) : T
}

case class CirceJsonConverter() extends JsonConverter{
  override def toJson[T](t: T): String = t.asJson.noSpaces
  override def fromJson[T](s: String): T = decode[T](s).getOrElse(null).asInstanceOf[T]
}

这样做的目的是简单地能够使用任何对象调用 JsonConverter 并将其转换为/从 json 转换jsonConverter.toJson(0) must equalTo("0"),但是当我尝试编译它时,我得到以下信息:

[error] could not find implicit value for parameter encoder: io.circe.Encoder[T]
[error]   override def toJson[T](t: T): String = t.asJson.noSpaces
[error]                                            ^
[error] could not find implicit value for parameter decoder: io.circe.Decoder[T]
[error]   override def fromJson[T](s: String): T = decode[T](s).getOrElse(null).asInstanceOf[T]
[error]                                                     ^
[error] two errors found

我当然可以拥有一个我打算通过转换器继承的所有内容的类,但我的印象是 circe 可以自动生成编码器/解码器?

4

2 回答 2

4

除非您可以实施将任何对象转换为 Json 的策略,否则您想要的不会起作用……这似乎不太可能。相反,Circe(和许多其他库)选择使用称为 Type Classes 的通用模式来方便地定义您想要如何做某事,在这种情况下Encoder/ Decoder,用于特定 type

如果您不熟悉类型类,我建议您研究它们。然后查看 Circe 文档,了解如何具体实现编码器/解码器。

于 2016-09-22T21:12:07.343 回答
4

在我的重复问题中,按照 Idan Waisman 的回答和 C4stor的回答,我使用了 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 解码的包(例如)中,如果不存在,我们将创建包对象并使其扩展Autotrait 并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.jsonAuto隐含在范围内
  • 您可以获得JsonDecoder[T]任何T具有io.circe.Decoder[T]或可以使用Auto隐式生成的类型
  • 您不需要io.circe.generic.auto._在每个文件中导入
  • com.example.app.json您可以通过仅更改包对象内容在 json 库之间切换。

例如,您可以切换到json4s(尽管我做了相反的事情并从 json4s 切换到 circe)。实现提供者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]
}

使用类型类模式,您可以获得编译时依赖注入。与运行时依赖注入相比,这给您的灵活性较低,但我怀疑您是否需要在运行时切换 json 解析器。

于 2016-12-12T13:39:34.993 回答