How to represent optional fields in spray-json?

杀马特。学长 韩版系。学妹 提交于 2019-12-20 17:37:38

问题


I have an optional field on my requests:

case class SearchRequest(url: String, nextAt: Option[Date])

My protocol is:

object SearchRequestJsonProtocol extends DefaultJsonProtocol {
    implicit val searchRequestFormat = jsonFormat(SearchRequest, "url", "nextAt")
}

How do I mark the nextAt field optional, such that the following JSON objects will be correctly read and accepted:

{"url":"..."}
{"url":"...", "nextAt":null}
{"url":"...", "nextAt":"2012-05-30T15:23Z"}

I actually don't really care about the null case, but if you have details, it would be nice. I'm using spray-json, and was under the impression that using an Option would skip the field if it was absent on the original JSON object.


回答1:


You might have to create an explicit format (warning: 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:


Works for me (spray-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:


Use NullOptions trait to disable skipping nulls: https://github.com/spray/spray-json#nulloptions Example: https://github.com/spray/spray-json/blob/master/src/test/scala/spray/json/ProductFormatsSpec.scala




回答4:


Don't know if this will help you but you can give that field a default value in the case class definition, so if the field is not in the json, it will assign the default value to it.




回答5:


Easy.

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:


For anyone who is chancing upon this post and wants an update to François Beausoleil's answer for newer versions of Spray (circa 2015+?), JsField is deprecated as a public member of JsValue; you should simply supply a list of tuples instead of JsFields. Their answer is spot-on, though.



来源:https://stackoverflow.com/questions/10819344/how-to-represent-optional-fields-in-spray-json

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!