Scala Circe与泛型

时间:2016-09-22 20:40:38

标签: json scala circe

我正在尝试使用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可以自动生成编码器/解码器?

2 个答案:

答案 0 :(得分:4)

除非你可以实现将任何对象转换为Json的策略,否则你想要的东西是行不通的......这似乎不太可能。 Circe(以及许多其他库)改为选择使用名为Type Classes的通用模式,以便于定义您想要执行某些操作的方式,在本例中为Encoder / Decoder,用于特定类型

如果您不熟悉类型类,我建议您进行研究。然后查看Circe文档,了解如何专门实现编码器/解码器。

答案 1 :(得分:3)

在我的副本answer中跟随Idan Waisman回答和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]
}

接下来在使用json解码的包(例如com.example.app.json)中,我们创建包对象(如果不存在)并使其扩展Auto trait并为给定提供隐式返回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]类型Tio.circe.Decoder[T]暗示可生成的Auto类别,您可以获得io.circe.generic.auto._
  • 您无需在每个文件中导入com.example.app.json
  • 您只需更改JsonDecoder[T]包对象内容即可在json库之间切换。

例如,你可以切换到json4s(虽然我做了相反的事情并从json4s切换到circe)。实施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] } 包对象内容更改为:

export class TblColabAdmin {
    snomatrcompl: string;
    nflativo: number;
    ativo: boolean;
}

使用Type Classes模式,您将获得编译时依赖项注入。这比运行时依赖注入的灵活性低,但我怀疑你需要在运行时切换json解析器。