我想弄清楚如何在使用路由指令完成時調用自定義的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)
感覺不對。
你在哪裏有'import MyJsonProtocol._'? – Gangstead 2014-09-19 19:07:57
已編輯的路徑片段明確顯示導入 – Rich 2014-09-19 20:56:41