2014-09-19 26 views
1

我想弄清楚如何在使用路由指令完成時調用自定義的JsonFormat寫入方法。使用jsonFormat創建的JsonFormat輔助函數可以正常工作,但是定義完整的JsonFormat不會被調用。spray-json和spray-routing:如何調用JsonFormat寫入完整

sealed trait Error 
sealed trait ErrorWithReason extends Error { 
    def reason: String 
} 

case class ValidationError(reason: String) extends ErrorWithReason 
case object EntityNotFound extends Error 
case class DatabaseError(reason: String) extends ErrorWithReason 

case class Record(a: String, b: String, error: Error) 

object MyJsonProtocol extends DefaultJsonProtocol { 
    implicit object ErrorJsonFormat extends JsonFormat[Error] { 
    def write(err: Error) = failure match { 
     case e: ErrorWithReason => JsString(e.reason) 
     case x => JsString(x.toString()) 
    } 
    def read(value: JsValue) = { 
     value match { 
     //Really only intended to serialize to JSON for API responses, not implementing read 
     case _ => throw new DeserializationException("Can't reliably deserialize Error") 
     } 
    } 
    } 

    implicit val record2Json = jsonFormat3(Record) 
} 

然後像一個路線:

import MyJsonProtocol._ 

trait TestRoute extends HttpService with Json4sSupport { 
    path("testRoute") { 
    val response: Record = getErrorRecord() 
    complete(response) 
    } 
} 

如果我添加日誌,我可以看到ErrorJsonFormat.write方法不會被調用。

後果如下所示,顯示了我想要獲得的輸出以及我實際獲得的輸出。比方說,記錄實例是記錄( 「東西」, 「somethingelse」,EntityNotFound)

實際

{ 
    "a": "something", 
    "b": "somethingelse", 
    "error": {} 
} 

{ 
    "a": "something", 
    "b": "somethingelse", 
    "error": "EntityNotFound" 
} 

我期待的是,complete(record)使用隱式JsonFormat for Record,它依次依賴隱式對象ErrorJsonFormat,該對象指定創建適當JsSt的寫入方法環場。相反,它似乎都能識別提供的ErrorJsonFormat,而忽略其序列化指令。

我覺得應該是不涉及需要有一個明確的implicit object RecordJsonFormat extends JsonFormat[Record] { ... }

所以更換implicit val record2Json = jsonFormat3(Record)總結一下我問

  • 爲什麼記錄的序列化不能稱之爲解決方案ErrorJsonFormat寫入方法(它甚至會做什麼呢?)回答下面
  • 有沒有辦法符合我的期望,同時仍然使用complete(record)

編輯

通過噴霧JSON源代碼挖掘,有一個SBT-樣板模板,似乎定義jsonFormat一系列的方法:https://github.com/spray/spray-json/blob/master/src/main/boilerplate/spray/json/ProductFormatsInstances.scala.template

和jsonFormat3相關產品從這似乎是:

def jsonFormat3[P1 :JF, P2 :JF, P3 :JF, T <: Product :ClassManifest](construct: (P1, P2, P3) => T): RootJsonFormat[T] = { 
    val Array(p1,p2,p3) = extractFieldNames(classManifest[T]) 
    jsonFormat(construct, p1, p2, p3) 
} 

def jsonFormat[P1 :JF, P2 :JF, P3 :JF, T <: Product](construct: (P1, P2, P3) => T, fieldName1: String, fieldName2: String, fieldName3: String): RootJsonFormat[T] = new RootJsonFormat[T]{ 
    def write(p: T) = { 
    val fields = new collection.mutable.ListBuffer[(String, JsValue)] 
    fields.sizeHint(3 * 4) 

    fields ++= productElement2Field[P1](fieldName1, p, 0) 
    fields ++= productElement2Field[P2](fieldName2, p, 0) 
    fields ++= productElement2Field[P3](fieldName3, p, 0) 

    JsObject(fields: _*) 
    } 
    def read(value: JsValue) = { 
    val p1V = fromField[P1](value, fieldName1) 
    val p2V = fromField[P2](value, fieldName2) 
    val p3V = fromField[P3](value, fieldName3) 

    construct(p1v, p2v, p3v) 
    } 
} 

從這看起來,似乎jsonFormat3本身是完全沒問題的(如果你追蹤到productElement2Field它抓住作家並直接調用寫入)。然後問題就在於complete(record)根本不涉及JsonFormat,並以某種方式交替地整理對象。

所以這似乎回答第1部分:爲什麼記錄的序列化失敗,調用ErrorJsonFormat寫入方法(這是什麼連做呢?)。沒有JsonFormat被稱爲是因爲通過其他方式完成了元帥。

看來剩下的問題是,如果可以爲完整的指令提供一個編組器,如果它存在,將使用JsonFormat,否則默認爲其正常行爲。我意識到我通常可以依靠基本案例類序列化的默認編組器。但是當我在這個例子中遇到複雜的trait/case類設置時,我需要使用JsonFormat來獲得正確的響應。理想情況下,對於需要了解默認編組器的情況而不需要調用JsonFormat的情況,這種區分不應該是明確的。換句話說,需要區分給定類型是否需要寫爲complete(someType)complete(someType.toJson)感覺不對。

+0

你在哪裏有'import MyJsonProtocol._'? – Gangstead 2014-09-19 19:07:57

+0

已編輯的路徑片段明確顯示導入 – Rich 2014-09-19 20:56:41

回答

0

經過深入挖掘,似乎問題的根源是代碼中Json4s和Spray-Json庫的混淆。在試圖追蹤JSON處理的各種元素的例子時,我並沒有很快意識到兩個庫之間的分離,最後是代碼混合了每個元素,解釋了意外的行爲。

在這個問題中,有問題的作品正在拉動路由器中的Json4sSupport。正確的定義應該使用SprayJsonSupport:

import MyJsonProtocol._ 

trait TestRoute extends HttpService with SprayJsonSupport { 
    path("testRoute") { 
    val response: Record = getErrorRecord() 
    complete(response) 
    } 
} 

考慮到這一切,答案更加明顯。

1:爲什麼Record的序列化無法調用ErrorJsonFormat的寫入方法(它甚至會做什麼)?

沒有JsonFormat被調用,因爲通過一些其他方式完成marshals。另一種方法是Json4s與Json4sSupport隱含提供的封送處理。您可以使用record.toJson強制對象的噴出json序列化,但輸出不會乾淨(它將包含嵌套JS對象和「字段」鍵)。

  1. 在仍使用完整(記錄)的情況下是否有符合我的期望的方法?

是,使用SprayJsonSupport將使用隱RootJsonReader和/或在需要時自動創建一個相關的Unmarshaller和/或的Marshaller RootJsonWriter。 Documentation reference

所以使用SprayJsonSupport時,它會看到由jsonFormat3(Record)complete(record)定義的RootJsonWriter將按預期序列化。