Scala spray-json和spray-routing:如何调用JsonFormat-write-in-complete
我试图弄清楚如何在使用routing指令complete时调用定制的JsonFormat write方法。使用JsonFormat帮助函数集创建的JsonFormat工作正常,但不会调用定义完整的JsonFormatScala spray-json和spray-routing:如何调用JsonFormat-write-in-complete,scala,spray,spray-json,Scala,Spray,Spray Json,我试图弄清楚如何在使用routing指令complete时调用定制的JsonFormat write方法。使用JsonFormat帮助函数集创建的JsonFormat工作正常,但不会调用定义完整的JsonFormat sealed trait Error sealed trait ErrorWithReason extends Error { def reason: String } case class ValidationError(reason: String) extends Er
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方法从未被调用
结果如下所示,显示了我试图获得的输出和实际获得的输出。假设记录实例是Record(“something”,“somethingelse”,EntityNotFound)
实际值
{
"a": "something",
"b": "somethingelse",
"error": {}
}
预定的
{
"a": "something",
"b": "somethingelse",
"error": "EntityNotFound"
}
我希望complete(record)
使用隐式JsonFormat for record,这反过来又依赖于隐式对象ErrorJsonFormat,它指定了创建适当JsString字段的write方法。相反,它似乎都能识别提供的ErrorJsonFormat,而忽略了序列化的指令
我觉得应该有一个解决方案,它不需要将隐式val record2Json=jsonFormat3(Record)
替换为显式隐式对象RecordJsonFormat extensed JsonFormat[Record]{…}
总结一下我的问题
- 为什么记录的序列化无法调用ErrorJsonFormat写入方法(它甚至做了什么?下面回答了
- 在仍然使用
的情况下,是否有办法满足我的期望complete(record)
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)
}
}
defjsonformat3[P1:JF,P2:JF,P3:JF,tt]:RootJsonFormat[T]={
val数组(p1、p2、p3)=提取字段名(类清单[T])
jsonFormat(构造、p1、p2、p3)
}
def jsonFormat[P1:JF,P2:JF,P3:JF,tt,fieldName1:String,fieldName2:String,fieldName3:String):RootJsonFormat[T]=新的RootJsonFormat[T]{
def写入(p:T)={
val fields=new collection.mutable.ListBuffer[(字符串,JsValue)]
fields.sizeHint(3*4)
fields++=productElement2Field[P1](fieldName1,p,0)
fields++=productElement2Field[P2](fieldName2,p,0)
fields++=productElement2Field[P3](fieldName3,p,0)
JsObject(字段:*)
}
def读取(值:JsValue)={
val p1V=fromField[P1](值,fieldName1)
val p2V=fromField[P2](值,fieldName2)
val p3V=fromField[P3](值,字段名3)
构造(p1v、p2v、p3v)
}
}
由此看来,jsonFormat3本身似乎很好(如果您跟踪productElement2Field,它会抓住writer并直接调用write)。问题一定是complete(record)
根本不涉及JsonFormat,而是以某种方式交替封送对象
因此,这似乎回答了第1部分:为什么记录序列化无法调用ErrorJsonFormat写入方法(它甚至做了什么?。没有调用JsonFormat,因为通过其他方式完成封送处理
似乎剩下的问题是,是否有可能为完整指令提供封送拆收器,该指令将使用JsonFormat(如果存在),否则将默认为其正常行为。我意识到,我通常可以依赖默认封送拆收器进行基本的案例类序列化。但是,当我得到一个复杂的trait/case类设置,如在本例中,我需要使用JsonFormat来获得正确的响应。理想情况下,对于编写路由的人来说,这种区别不应该是明确的,因为他们需要知道默认封送器的位置,而不是需要调用JsonFormat。或者换句话说,需要区分给定类型是否需要作为
com编写完成(someType)
或完成(someType.toJson)
感觉不对。进一步挖掘之后,问题的根源似乎是代码中Json4s和Spray Json库的混淆。在试图追踪Json处理的各种元素的示例时,我没有意识到这两个库之间的分离,结果是代码混合了其中的一些,解释如下对意外行为的惩罚
在这个问题中,有问题的部分是拉入路由器中的Json4sSupport。正确的定义应该是使用SprayJsonSupport:
import MyJsonProtocol._
trait TestRoute extends HttpService with SprayJsonSupport {
path("testRoute") {
val response: Record = getErrorRecord()
complete(response)
}
}
综上所述,答案更加明显
1:为什么记录的序列化无法调用ErrorJsonFormat写入方法(它甚至做了什么)?
没有调用JsonFormat,因为通过其他方式完成封送。其他方式是Json4s使用Json4sSupport隐式提供的封送。您可以使用record.toJson
强制对对象进行json序列化,但输出将不干净(它将包括嵌套的JS对象和“字段”键)
因此,使用SprayJsonSupport,它将看到由
jsonFormat3(记录)
和complete(记录)
定义的RootJsonWriter将按预期进行序列化。哪里有import MyJsonProtocol.\u
编辑的路由片段以显式显示导入