From 20afa757f499688b56cbd6b8571014b75d6d4dce Mon Sep 17 00:00:00 2001 From: Jakob Odersky Date: Thu, 2 Aug 2018 15:24:52 -0700 Subject: Treat value classes transparently --- shared/src/main/scala/DerivedFormats.scala | 24 ++++++++++++-------- shared/src/test/scala/ProductTypeFormatTests.scala | 26 +++++++++++++++++++++- 2 files changed, 40 insertions(+), 10 deletions(-) diff --git a/shared/src/main/scala/DerivedFormats.scala b/shared/src/main/scala/DerivedFormats.scala index f6a3f7b..ed39f46 100644 --- a/shared/src/main/scala/DerivedFormats.scala +++ b/shared/src/main/scala/DerivedFormats.scala @@ -24,18 +24,24 @@ trait DerivedFormats { self: BasicFormats => 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.collect { - case param - if !param.option || param.dereference(value) != None || printNull => - extractFieldName(param.label) -> param.typeclass.write( - param.dereference(value) - ) + override def write(value: T): JsValue = + if (ctx.isValueClass) { + val param = ctx.parameters.head + param.typeclass.write(param.dereference(value)) + } else { + 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: _*) } - JsObject(fields: _*) - } override def read(value: JsValue): T = value match { + case js if ctx.isValueClass => + ctx.construct(param => param.typeclass.read(value)) case obj: JsObject => if (ctx.isObject) { ctx.rawConstruct(Seq.empty) diff --git a/shared/src/test/scala/ProductTypeFormatTests.scala b/shared/src/test/scala/ProductTypeFormatTests.scala index a869ac5..d7721ba 100644 --- a/shared/src/test/scala/ProductTypeFormatTests.scala +++ b/shared/src/test/scala/ProductTypeFormatTests.scala @@ -90,11 +90,35 @@ class ProductTypeFormatTests case class Phantom[T](x: Int) // no json format required for T - implicit def phantom[T] = jsonFormat[Phantom[T]] + implicit def phantom[T]: RootJsonFormat[Phantom[T]] = jsonFormat[Phantom[T]] "Phantom types without a format" should behave like checkRoundtrip( Phantom[Int => String](42), // the given type parameter should not have a format """{"x":42}""" ) + implicit val vc = jsonFormat[ProductTypeFormatTests.Wrapper] + "Value classes" should behave like checkRoundtrip( + ProductTypeFormatTests.Wrapper(42), + """42""" + ) + + implicit def vcg[T: JsonFormat] = jsonFormat[ProductTypeFormatTests.WrapperGeneric[T]] + "Value classes with generic parameters" should behave like checkRoundtrip( + ProductTypeFormatTests.WrapperGeneric[F](F(42)), + """{"y": 42}""" + ) + + implicit def vcp[T] = jsonFormat[ProductTypeFormatTests.WrapperPhantom[T]] + "value classes with unused generic parameters" should behave like checkRoundtrip( + ProductTypeFormatTests.WrapperPhantom[Int => String](42), + """42""" + ) + +} + +object ProductTypeFormatTests { + case class Wrapper(x: Int) extends AnyVal + case class WrapperGeneric[A](x: A) extends AnyVal + case class WrapperPhantom[A](x: Int) extends AnyVal } -- cgit v1.2.3