2017-10-06 82 views
0

我有這樣的模型:兩個枚舉並與這些枚舉類型的兩個領域的一個案例類:斯卡拉,噴霧JSON:通用枚舉JSON格式

// see later, why objects are implicit 
implicit object Fruits extends Enumeration { 
    val Apple = Value("apple") 
    val Orange = Value("orange") 
} 

implicit object Vegetables extends Enumeration { 
    val Potato = Value("potato") 
    val Cucumber = Value("cucumber") 
    val Tomato = Value("tomato") 
} 

type Fruit = Fruits.Value 
type Vegetable = Vegetables.Value 

case class Pair(fruit: Fruit, vegetable: Vegetable) 

我想分析/生成雙JSONs到/用噴霧罐。我不想爲水果和蔬菜申報單獨的JsonFormat。所以,我希望做這樣的事情:

import spray.json._ 
import spray.json.DefaultJsonProtocol._ 

// enum is implicit here, that's why we needed implicit objects 
implicit def enumFormat[A <: Enumeration](implicit enum: A): RootJsonFormat[enum.Value] = 
    new RootJsonFormat[enum.Value] { 
    def read(value: JsValue): enum.Value = value match { 
     case JsString(s) => 
     enum.withName(s) 
     case x => 
     deserializationError("Expected JsString, but got " + x) 
    } 

    def write(obj: enum.Value) = JsString(obj.toString) 
    } 

// compilation error: couldn't find implicits for JF[Fruit] and JF[Vegetable] 
implicit val pairFormat = jsonFormat2(Pair) 

// expected value: 
// spray.json.JsValue = {"fruit":"apple","vegetable":"potato"} 
// but actually doesn't even compile 
Pair(Fruits.Apple, Vegetables.Potato).toJson 

可悲的是,enumFormat不會爲jsonFormat2產生隱含值。如果我寫pairFormat前水果和蔬菜的格式手動兩個隱含的聲明,然後JSON編組工作:

implicit val fruitFormat: RootJsonFormat[Fruit] = enumFormat(Fruits) 
implicit val vegetableFormat: RootJsonFormat[Vegetable] = enumFormat(Vegetables) 

implicit val pairFormat = jsonFormat2(Pair) 

// {"fruit":"apple","vegetable":"potato"}, as expected 
Pair(Fruits.Apple, Vegetables.Potato).toJson 

於是,兩個問題:

  1. 如何擺脫這些fruitFormatvegetableFormat聲明?

  2. 理想情況下,不要讓枚舉對象隱含,同時保持enumFormat函數是通用的。有沒有辦法做到這一點?也許,使用scala.reflect包或類似的東西。

回答

1

您只需將enum.Value替換爲A#Value即可。

看着spray-json #200,你可以找到一個良好定義的隱enumFormat一個例子,略作修改,以充分利用隱含enu檢索:

implicit def enumFormat[T <: Enumeration](implicit enu: T): RootJsonFormat[T#Value] = 
    new RootJsonFormat[T#Value] { 
    def write(obj: T#Value): JsValue = JsString(obj.toString) 
    def read(json: JsValue): T#Value = { 
     json match { 
     case JsString(txt) => enu.withName(txt) 
     case somethingElse => throw DeserializationException(s"Expected a value from enum $enu instead of $somethingElse") 
     } 
    } 
} 
+0

這應該是正確的答案。不明白爲什麼它被拒絕投票。 – PH88

0

你不能,斯卡拉不允許隱式鏈接,因爲它會導致組合爆炸,將使編譯器速度太慢。

參見https://docs.scala-lang.org/tutorials/FAQ/chaining-implicits.html

Scala中不允許兩個這樣的隱式轉換使用隱式A至B和其他隱式B到C

發生,但是,因此不能從A獲得至C

您必須爲每個想要使用的T明確產生一個JsonFormat[T]