2016-09-22 84 views
3

我试图使用Scala的JSON库瑟茜,在简单的特点,提供从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 

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

回答

4

除非您可以实施将任何对象转换为Json的策略,否则您想要的是不可行的。 Circe(和许多其他库)选择使用称为“类别类”的常见模式,以便于定义您想要做什么的方式,在此例中为Encoder/Decoder,对于特定类型

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

+1

为了记录,io.circe.generic.auto._的导入是生成实例的一种便捷方式,但它并不奇迹般地为任何东西和所有东西创建实例。它只会使它们成为用该导入文件中定义的案例类。 –

1

关注Idan Waisman答案和C4stor answer在我的副本中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对象内容并创建一个性状(I制成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 implicits你可以得到JsonDecoder[T]对于具有io.circe.Decoder[T]或者它可以生成任何类型的T d与Auto implicits
  • 你不需要仅通过改变com.example.app.json包对象的内容导入io.circe.generic.auto._中的每个文件
  • 可以JSON库之间进行切换。

例如,你可以切换到json4s(尽管我做了相反的事情,并从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] 
} 

随着类型类的模式你编译时依赖注入。这给你比运行时依赖注入更少的灵活性,但我怀疑你需要在运行时切换json解析器。