[英]Scala Circe with generics
I am trying to use the scala json library Circe, wrapping it in a simple trait to provide conversion to/from json for which I have the following: 我正在尝试使用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]
}
The aim of this is to simply be able to call JsonConverter with any object and have it convert it to/from json as such jsonConverter.toJson(0) must equalTo("0")
, however when I try to compile it I get the following: 这样做的目的是简单地能够使用任何对象调用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
I can of course have a class that everything I intend to put through the converter inherit from, but I had the impression that circe could auto generate the encoders/decoders? 我当然可以有一个类,我打算通过转换器继承的所有东西,但我有一个印象,circe可以自动生成编码器/解码器?
What you want is not going to work unless you can implement a strategy for turning any object into Json... which seems unlikely. 除非你可以实现将任何对象转换为Json的策略,否则你想要的东西是行不通的......这似乎不太可能。 Circe (and many other libs) instead choose to use a common pattern called Type Classes to make it convenient to define how you want to do something, in this case
Encoder
/ Decoder
, for a specific type . Circe(以及许多其他库)改为选择使用名为Type Classes的通用模式,以便于定义您想要执行某些操作的方式,在本例中为特定类型的
Encoder
/ Decoder
。
I recommend researching Type Classes if you are unfamiliar with them. 如果您不熟悉它,我建议研究类型类。 And then take a look at the Circe docs to see how you can implement Encoders/Decoders specifically.
然后查看Circe文档,了解如何专门实现编码器/解码器。
Following Idan Waisman answer and C4stor answer in my duplicate question I used the Type Classes pattern. 继Idan Waisman回答和C4stor在我的重复问题中 回答后 ,我使用了Type Classes模式。 For brevity I provide sample code only for decoding json.
为简洁起见,我提供的示例代码仅用于解码json。 Encoding can be implemented in exactly the same way.
编码可以以完全相同的方式实现。
First, let's define the trait that will be used to inject json decoder dependency: 首先,让我们定义将用于注入json解码器依赖的特征:
trait JsonDecoder[T] {
def apply(s: String): Option[T]
}
Next we define object that creates instance implementing this trait: 接下来,我们定义创建实现此特征的实例的对象:
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))
}
}
As you can notice apply
requires implicit io.circe.Decoder[T]
to be in scope when it called. 正如你可以看到
apply
要求隐式io.circe.Decoder[T]
在范围上,当它被调用。
Then we copy io.circe.generic.auto
object content and create a trait (I made PR to have this trait available as io.circe.generic.Auto
): 然后我们复制
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]
}
Next in the package (eg com.example.app.json
) that uses json decoding a lot we create package object if does not exist and make it extend Auto
trait and provide implicit returning JsonDecoder[T]
for given type T
: 接下来在包中使用json解码的包(例如
com.example.app.json
),如果不存在则创建包对象并使其扩展Auto
trait并为给定类型T
提供隐式返回JsonDecoder[T]
:
package com.example.app
import io.circe.Decoder
package object json extends Auto {
implicit def decoder[T: Decoder]: JsonDecoder[T] = CirceDecoderProvider[T]
}
Now: 现在:
com.example.app.json
has Auto
implicits in scope com.example.app.json
所有源文件都在范围内具有Auto
implicits JsonDecoder[T]
for any type T
that has io.circe.Decoder[T]
or for which it can be generated with Auto
implicits io.circe.Decoder[T]
类型T
获取JsonDecoder[T]
,或者可以使用Auto
implicits生成它 io.circe.generic.auto._
in every file io.circe.generic.auto._
com.example.app.json
package object content. com.example.app.json
包对象内容即可在json库之间切换。 For example you can switch to json4s (though I did the opposite and switched to circe from json4s). 例如,您可以切换到json4s (虽然我做了相反的事情并从json4s切换到circe)。 Implement provider for
JsonDecoder[T]
: 为
JsonDecoder[T]
实现提供程序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
}
}
}
And change com.example.app.json
package object content to: 并将
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]
}
With Type Classes pattern you get compile-time dependency injection. 使用Type Classes模式,您可以获得编译时依赖项注入。 That gives you less flexibility than runtime dependency injection but I doubt that you need to switch json parsers in runtime.
这比运行时依赖注入的灵活性低,但我怀疑你需要在运行时切换json解析器。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.