2014-09-19 4 views
1

Я пытаюсь выяснить, как получить настраиваемый метод записи 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) чувствует себя не так.

+0

Где вы импортируете MyJsonProtocol._'? – Gangstead

+0

Отредактированный фрагмент маршрута, чтобы явно показать импорт – Rich

ответ

0

После копания, кажется, корень проблемы был путаницей библиотек Json4s и Spray-Json в коде. Пытаясь отследить примеры различных элементов обработки JSON, я не мог легко распознать разделение между двумя библиотеками и в итоге оказался с кодом, который смешивал некоторые из них, объясняя неожиданное поведение.

В этом вопросе оскорбительная деталь тянет Json4sSupport в маршрутизаторе. Правильное определение должно быть использовано SprayJsonSupport:

import MyJsonProtocol._ 

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

С учетом всего этого ответы более очевидны.

1: Почему сериализация записи не позволяет вызвать метод записи ErrorJsonFormat (что он делает вместо этого)?.

No JsonFormat называется, потому что полные маршалы с помощью некоторых других средств. Другими средствами является маршалинг, предоставляемый неявно Json4s с Json4sSupport. Вы можете использовать record.toJson для принудительной сериализации объекта с помощью распыления, но вывод не будет чистым (он будет включать вложенные объекты JS и клавиши «поля»).

  1. Есть ли способ соответствовать моим ожиданиям при использовании полного (записи)?

Да, с помощью SprayJsonSupport будет использовать неявное RootJsonReader и/или RootJsonWriter, где это необходимо для автоматического создания соответствующего Unmarshaller и/или ИАС. Documentation reference

Таким образом, с помощью SprayJsonSupport он увидит, что RootJsonWriter, определенный jsonFormat3(Record), и complete(record) будет сериализоваться, как ожидалось.