2016-09-22 114 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解析器。