Я пытаюсь выяснить, как получить настраиваемый метод записи JsonFormat для вызова при использовании директивы маршрутизации. JsonFormat, созданный с помощью набора jsonFormat вспомогательных функций, работает нормально, но определение полного JsonFormat не будет вызвано.спрей-джсон и спрей-маршрутизация: как вызвать 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 для записи, который, в свою очередь, полагается на неявный объект ErrorJsonFormat, который определяет метод записи, который создает соответствующий JsSt кольцо. Вместо этого, как представляется, оба признают предоставленный ErrorJsonFormat, игнорируя его инструкции для сериализации.
Я чувствую, что там должно быть решение, которое не предполагает необходимости заменить implicit val record2Json = jsonFormat3(Record)
с явным implicit object RecordJsonFormat extends JsonFormat[Record] { ... }
Итак, подведем итог, что я прошу
- Почему сериализации записи не называть метод write 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, если она существует в противном случае по умолчанию для ее нормального поведения. Я понимаю, что я могу вообще полагаться на маршаллер по умолчанию для базовой сериализации класса case. Но когда я получаю сложную настройку класса trait/case, как в этом примере, мне нужно использовать JsonFormat для получения правильного ответа. В идеале это различие не должно быть явным для тех, кто пишет маршруты, чтобы знать ситуации, когда его маршаллер по умолчанию в отличие от необходимости запускать JsonFormat. Или, другими словами, нужно различать, должен ли данный тип быть записан как complete(someType)
или complete(someType.toJson)
чувствует себя не так.
Где вы импортируете MyJsonProtocol._'? – Gangstead
Отредактированный фрагмент маршрута, чтобы явно показать импорт – Rich