将spray与spray-json一起用于系统,版本:
"io.spray" %% "spray-json" % "1.2.6"
我无法想象如何让自定义JsonFormat定义适用于喷射路由处理的序列化。
我有两个失败的情况。
1。嵌套案例类
基本案例类JSON序列化工作正常
case class Something(a: String, b: String)
implicit val something2Json = jsonFormat3(Something)
但是如果我在case类中有一个嵌套的case类要序列化,我可以通过提供另一个隐含的JsonFormat来解决编译问题,但是在运行时它拒绝序列化
case class Subrecord(value: String)
case class Record(a: String, b: String, subrecord: Subrecord)
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object SubrecordJsonFormat extends JsonFormat[Subrecord] {
def write(sub: Subrecord) = JsString(sub.value)
def read(value: JsValue) = value match {
case JsString(s) => Subrecord(s)
case _ => throw new DeserializationException("Cannot parse Subrecord")
}
}
implicit val record2Json = jsonFormat3(Record)
}
这会在运行时抛出MappingException,说明子记录没有可用的值
2。具有各种0-N案例扩展的特征
这里我有一个特征,可以作为一组案例类的捕获类型。一些扩展类具有val,而其他类没有val并且是对象。当序列化发生时,似乎我的隐式定义的JsonFormat被完全忽略了,我只是给了一个空的JsObject,特别是当实际的底层类型是没有val的case对象之一时。
sealed trait Errors
sealed trait ErrorsWithReason extends Errors {
def reason: String
}
case class ValidationError(reason: String) extends ErrorsWithReason
case object EntityNotFound extends Errors
case class DatabaseError(reason: String) extends ErrorsWithReason
object MyJsonProtocol extends DefaultJsonProtocol {
implicit object ErrorsJsonFormat extends JsonFormat[Errors] {
def write(err: Errors) = failure match {
case e: ErrorsWithReason => JsString(e.reason)
case x => JsString(x.toString())
}
def read(value: JsValue) = {
value match {
//Really only intended to serialize to JSON for API responses
case _ => throw new DeserializationException("Can't reliably deserialize Error")
}
}
}
}
因此,如上所述,如果序列化的实际类型是EntityNotFound,则序列化将变为RootJsonFormat,变为{}
。如果它是一个ErrorsWithReason,那么它变成一个RootJsonFormat变成{ "reason": "somevalue" }
。我可能会对JsonFormat定义应该如何工作感到困惑,但它似乎根本没有使用我的write方法,而是突然想出了如何自行序列化。
修改
特定序列化案例正在使用读取/反序列化,如:
entity(as[JObject]) { json =>
val extraction: A = json.extract[A]
}
使用complete
指令编写/序列化。
我现在意识到这里发布的第一个答案是我的JsonDefaultProtocol和JsonFormat实现用于spray-json类,同时反序列化中的实体指令提取使用json4s JObject而不是spray-json JsObject。
答案 0 :(得分:3)
清洁JSON输出的另一种方法
import spray.json._
import spray.json.DefaultJsonProtocol._
// #1. Subrecords
case class Subrecord(value: String)
case class Record(a: String, b: String, subrecord: Subrecord)
implicit object RecordFormat extends JsonFormat[Record] {
def write(obj: Record): JsValue = {
JsObject(
("a", JsString(obj.a)),
("b", JsString(obj.b)),
("reason", JsString(obj.subrecord.value))
)
}
def read(json: JsValue): Record = json match {
case JsObject(fields)
if fields.isDefinedAt("a") & fields.isDefinedAt("b") & fields.isDefinedAt("reason") =>
Record(fields("a").convertTo[String],
fields("b").convertTo[String],
Subrecord(fields("reason").convertTo[String])
)
case _ => deserializationError("Not a Record")
}
}
val record = Record("first", "other", Subrecord("some error message"))
val recordToJson = record.toJson
val recordFromJson = recordToJson.convertTo[Record]
println(recordToJson)
assert(recordFromJson == record)
答案 1 :(得分:1)
如果你需要读写,你可以这样做:
import spray.json._
import spray.json.DefaultJsonProtocol._
// #1. Subrecords
case class Subrecord(value: String)
case class Record(a: String, b: String, subrecord: Subrecord)
implicit val subrecordFormat = jsonFormat1(Subrecord)
implicit val recordFormat = jsonFormat3(Record)
val record = Record("a", "b", Subrecord("c"))
val recordToJson = record.toJson
val recordFromJson = recordToJson.convertTo[Record]
assert(recordFromJson == record)
// #2. Sealed traits
sealed trait Errors
sealed trait ErrorsWithReason extends Errors {
def reason: String
}
case class ValidationError(reason: String) extends ErrorsWithReason
case object EntityNotFound extends Errors
case class DatabaseError(reason: String) extends ErrorsWithReason
implicit object ErrorsJsonFormat extends JsonFormat[Errors] {
def write(err: Errors) = err match {
case ValidationError(reason) =>
JsObject(
("error", JsString("ValidationError")),
("reason", JsString(reason))
)
case DatabaseError(reason) =>
JsObject(
("error", JsString("DatabaseError")),
("reason", JsString(reason))
)
case EntityNotFound => JsString("EntityNotFound")
}
def read(value: JsValue) = value match {
case JsString("EntityNotFound") => EntityNotFound
case JsObject(fields) if fields("error") == JsString("ValidationError") =>
ValidationError(fields("reason").convertTo[String])
case JsObject(fields) if fields("error") == JsString("DatabaseError") =>
DatabaseError(fields("reason").convertTo[String])
}
}
val validationError: Errors = ValidationError("error")
val databaseError: Errors = DatabaseError("error")
val entityNotFound: Errors = EntityNotFound
assert(validationError.toJson.convertTo[Errors] == validationError)
assert(databaseError.toJson.convertTo[Errors] == databaseError)
assert(entityNotFound.toJson.convertTo[Errors] == entityNotFound)