diff options
author | Jakob Odersky <jakob@driver.xyz> | 2018-06-29 17:56:06 -0700 |
---|---|---|
committer | Jakob Odersky <jakob@odersky.com> | 2018-07-02 16:28:46 -0700 |
commit | c5c1aa6bc78b6ebc346befe9f4b434401a683a59 (patch) | |
tree | fd5a92d7b89825d68496306d81dc7ebd2b2f1981 /shared/src | |
parent | 1c358416737f8e7d41d6858000ce07680df7afee (diff) | |
download | spray-json-derivation-c5c1aa6bc78b6ebc346befe9f4b434401a683a59.tar.gz spray-json-derivation-c5c1aa6bc78b6ebc346befe9f4b434401a683a59.tar.bz2 spray-json-derivation-c5c1aa6bc78b6ebc346befe9f4b434401a683a59.zip |
Make inclusion of None values as null optional
Diffstat (limited to 'shared/src')
-rw-r--r-- | shared/src/main/scala/DerivedFormats.scala | 19 | ||||
-rw-r--r-- | shared/src/test/scala/OptionFieldTests.scala | 53 | ||||
-rw-r--r-- | shared/src/test/scala/ProductTypeFormatTests.scala | 17 |
3 files changed, 68 insertions, 21 deletions
diff --git a/shared/src/main/scala/DerivedFormats.scala b/shared/src/main/scala/DerivedFormats.scala index 6ea7c4b..ccf48d1 100644 --- a/shared/src/main/scala/DerivedFormats.scala +++ b/shared/src/main/scala/DerivedFormats.scala @@ -13,13 +13,24 @@ trait DerivedFormats { self: BasicFormats => * method can be overriden to use alternative naming conventions. */ @inline def extractFieldName(paramName: String): String = paramName + /** Determines if `None` instances of options should be included in JSON output. + * + * By default, `None` values are ommitted entirely from resulting JSON + * objects. If overridden, they will be included as `null`s instead. + * + * Note that this has no effect in *reading* option types; undefined or null + * values are always converted to `None`. */ + def printNull: Boolean = false + def combine[T](ctx: CaseClass[JsonFormat, T]): JsonFormat[T] = new JsonFormat[T] { override def write(value: T): JsValue = { - val fields: Seq[(String, JsValue)] = ctx.parameters.map { param => - extractFieldName(param.label) -> param.typeclass.write( - param.dereference(value) - ) + val fields: Seq[(String, JsValue)] = ctx.parameters.collect { + case param + if !param.option || param.dereference(value) != None || printNull => + extractFieldName(param.label) -> param.typeclass.write( + param.dereference(value) + ) } JsObject(fields: _*) } diff --git a/shared/src/test/scala/OptionFieldTests.scala b/shared/src/test/scala/OptionFieldTests.scala new file mode 100644 index 0000000..8cabf25 --- /dev/null +++ b/shared/src/test/scala/OptionFieldTests.scala @@ -0,0 +1,53 @@ +package spray.json + +import org.scalatest._ + +class OptionFieldTests + extends FlatSpec + with FormatTests { + + case class Opt(x: Option[Int]) + + object HideNull extends DerivedJsonProtocol { + override def printNull = false + implicit val optFmt = jsonFormat[Opt] + } + + object ShowNull extends DerivedJsonProtocol { + override def printNull = true + implicit val optFmt = jsonFormat[Opt] + } + + + "Option fields with some value" should behave like checkRoundtrip( + Opt(Some(2)), + """{"x":2}""" + )(HideNull.optFmt) + + "Option fields with some value (show null)" should behave like checkRoundtrip( + Opt(Some(2)), + """{"x":2}""" + )(ShowNull.optFmt) + + "Option fields with null value" should behave like checkRoundtrip( + Opt(None), + """{}""" + )(HideNull.optFmt) + + "Option fields with null value (show null)" should behave like checkRoundtrip( + Opt(None), + """{"x":null}""" + )(ShowNull.optFmt) + + "Option fields with undefined value" should "deserialize" in { + import HideNull._ + assert("{}".parseJson.convertTo[Opt] == Opt(None)) + } + + "Option fields with undefined value (show null)" should "deserialize" in { + import ShowNull._ + assert("{}".parseJson.convertTo[Opt] == Opt(None)) + } + +} + diff --git a/shared/src/test/scala/ProductTypeFormatTests.scala b/shared/src/test/scala/ProductTypeFormatTests.scala index 4c979c0..a869ac5 100644 --- a/shared/src/test/scala/ProductTypeFormatTests.scala +++ b/shared/src/test/scala/ProductTypeFormatTests.scala @@ -73,23 +73,6 @@ class ProductTypeFormatTests """{"h": {"x":true}}""" ) - case class Opt(x: Option[Int]) - implicit val optFmt = jsonFormat[Opt] - - "Option fields with some value" should behave like checkRoundtrip( - Opt(Some(2)), - """{"x":2}""" - ) - - "Option fields with null value" should behave like checkRoundtrip( - Opt(None), - """{"x":null}""" - ) - - "Option fields with undefined value" should "deserialize" in { - assert("{}".parseJson.convertTo[Opt] == Opt(None)) - } - case class Typed[T](t: T) implicit def typed[T: JsonFormat] = jsonFormat[Typed[T]] |