Как представить дополнительные поля в spray-json?
У меня есть необязательное поле в моих запросах:
case class SearchRequest(url: String, nextAt: Option[Date])
Мой протокол:
object SearchRequestJsonProtocol extends DefaultJsonProtocol {
implicit val searchRequestFormat = jsonFormat(SearchRequest, "url", "nextAt")
}
Как пометить поле nextAt дополнительным, чтобы следующие объекты JSON были правильно прочитаны и приняты:
{"url":"..."}
{"url":"...", "nextAt":null}
{"url":"...", "nextAt":"2012-05-30T15:23Z"}
На самом деле я действительно не забочусь о нулевом случае, но если у вас есть детали, было бы неплохо. Я использую спрей-json, и у меня создалось впечатление, что использование опции будет пропускать поле, если оно отсутствует на исходном объекте JSON.
Ответы
Ответ 1
Возможно, вам придется создать явный формат (предупреждение: psuedocodish):
object SearchRequestJsonProtocol extends DefaultJsonProtocol {
implicit object SearchRequestJsonFormat extends JsonFormat[SearchRequest] {
def read(value: JsValue) = value match {
case JsObject(List(
JsField("url", JsString(url)),
JsField("nextAt", JsString(nextAt)))) =>
SearchRequest(url, Some(new Instant(nextAt)))
case JsObject(List(JsField("url", JsString(url)))) =>
SearchRequest(url, None)
case _ =>
throw new DeserializationException("SearchRequest expected")
}
def write(obj: SearchRequest) = obj.nextAt match {
case Some(nextAt) =>
JsObject(JsField("url", JsString(obj.url)),
JsField("nextAt", JsString(nextAt.toString)))
case None => JsObject(JsField("url", JsString(obj.url)))
}
}
}
Ответ 2
Работает для меня (спрей-json 1.1.1 scala 2.9.1 build)
import cc.spray.json._
import cc.spray.json.DefaultJsonProtocol._
// string instead of date for simplicity
case class SearchRequest(url: String, nextAt: Option[String])
// btw, you could use jsonFormat2 method here
implicit val searchRequestFormat = jsonFormat(SearchRequest, "url", "nextAt")
assert {
List(
"""{"url":"..."}""",
"""{"url":"...", "nextAt":null}""",
"""{"url":"...", "nextAt":"2012-05-30T15:23Z"}""")
.map(_.asJson.convertTo[SearchRequest]) == List(
SearchRequest("...", None),
SearchRequest("...", None),
SearchRequest("...", Some("2012-05-30T15:23Z")))
}
Ответ 3
Использовать черту NullOptions
для отключения пропуска null
s:
https://github.com/spray/spray-json#nulloptions
Пример:
https://github.com/spray/spray-json/blob/master/src/test/scala/spray/json/ProductFormatsSpec.scala
Ответ 4
Не знаю, поможет ли это вам, но вы можете присвоить этому полю значение по умолчанию в определении класса case, поэтому, если поле не находится в json, оно присваивает ему значение по умолчанию.
Ответ 5
Легко.
import cc.spray.json._
trait MyJsonProtocol extends DefaultJsonProtocol {
implicit val searchFormat = new JsonWriter[SearchRequest] {
def write(r: SearchRequest): JsValue = {
JsObject(
"url" -> JsString(r.url),
"next_at" -> r.nextAt.toJson,
)
}
}
}
class JsonTest extends FunSuite with MyJsonProtocol {
test("JSON") {
val search = new SearchRequest("www.site.ru", None)
val marshalled = search.toJson
println(marshalled)
}
}
Ответ 6
Для тех, кто выбирает этот пост и хочет получить обновленный ответ Франсуа Босолея о более новых версиях Spray (около 2015+?), JsField устарела как публичный член JsValue; Вы должны просто предоставить список кортежей вместо JsFields. Их ответ точен, хотя.
Ответ 7
Я создал библиотеку для этой цели.
https://github.com/bigwheel/spray-json-member-presence
С MemberPresenceJsonProtocol None используется только для нулевого JSON, а не для отсутствия члена. Если вы не используете там класс MemberOption, я думаю, он будет вам полезен.