Skip to content

Commit aa0c78a

Browse files
authored
Merge pull request #85 from ncreep/fix-flat-format-bug
Fixing support for recursive ADTs and user-defined implicits
2 parents f1ba4e6 + 7026793 commit aa0c78a

File tree

4 files changed

+67
-6
lines changed

4 files changed

+67
-6
lines changed

build.sbt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import sbtcrossproject.CrossPlugin.autoImport.{crossProject, CrossType}
1+
import com.typesafe.tools.mima.core.{IncompatibleSignatureProblem, ProblemFilters}
2+
import sbtcrossproject.CrossPlugin.autoImport.{CrossType, crossProject}
23

34
ThisBuild / organization := "org.julienrf"
45

@@ -10,6 +11,11 @@ ThisBuild / versionPolicyIntention := Compatibility.BinaryAndSourceCompatible
1011
// Temporary, because version 10.0.0 was invalid
1112
ThisBuild / versionPolicyPreviousVersions := Seq("10.0.1")
1213

14+
ThisBuild / mimaBinaryIssueFilters ++= Seq(
15+
// package private method
16+
ProblemFilters.exclude[IncompatibleSignatureProblem]("julienrf.json.derived.DerivedOWritesUtil.makeCoProductOWrites")
17+
)
18+
1319
ThisBuild / developers := List(
1420
Developer(
1521
"julienrf",

library/src/main/scala/julienrf/json/derived/DerivedOWrites.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ trait DerivedOWritesInstances extends DerivedOWritesInstances1 {
3232
typeTag: TT[FieldType[K, L]]
3333
): DerivedOWrites[FieldType[K, L] :+: R, TT] =
3434
DerivedOWritesUtil.makeCoProductOWrites(
35-
(_, _) => writesL.value,
35+
(_, _) => writesL,
3636
owritesR,
3737
typeTag)
3838
}
@@ -77,7 +77,7 @@ trait DerivedOWritesInstances1 extends DerivedOWritesInstances2 {
7777
typeTag: TT[FieldType[K, L]]
7878
): DerivedOWrites[FieldType[K, L] :+: R, TT] =
7979
DerivedOWritesUtil.makeCoProductOWrites(
80-
owritesL.value.owrites,
80+
(tagOwrites, adapter) => owritesL.map(_.owrites(tagOwrites, adapter)),
8181
owritesR,
8282
typeTag)
8383
}
@@ -116,7 +116,7 @@ trait DerivedOWritesInstances3 {
116116

117117
private[derived] object DerivedOWritesUtil {
118118
def makeCoProductOWrites[K <: Symbol, L, R <: Coproduct, TT[A] <: TypeTag[A]](
119-
makeWritesL: (TypeTagOWrites, NameAdapter) => Writes[L],
119+
makeWritesL: (TypeTagOWrites, NameAdapter) => Lazy[Writes[L]],
120120
owritesR: Lazy[DerivedOWrites[R, TT]],
121121
typeTag: TT[FieldType[K, L]]
122122
): DerivedOWrites[FieldType[K, L] :+: R, TT] =
@@ -127,7 +127,7 @@ private[derived] object DerivedOWritesUtil {
127127
val derivedOwriteR = owritesR.value.owrites(tagOwrites, adapter)
128128

129129
OWrites[FieldType[K, L] :+: R] {
130-
case Inl(l) => tagOwrites.owrites(typeTag.value, writesL).writes(l)
130+
case Inl(l) => tagOwrites.owrites(typeTag.value, writesL.value).writes(l)
131131
case Inr(r) => derivedOwriteR.writes(r)
132132
}
133133
}

library/src/main/scala/julienrf/json/derived/typetags.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ object TypeTagReads {
139139
*
140140
* @param tagReads A way to decode the type tag value.
141141
*/
142-
//TODO docs on edge case
143142
def flat(tagReads: Reads[String]): TypeTagReads =
144143
new TypeTagReads {
145144
def reads[A](typeName: String, reads: Reads[A]): Reads[A] = {

library/src/test/scala/julienrf/json/derived/DerivedOFormatSuite.scala

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,5 +269,61 @@ class DerivedOFormatSuite extends AnyFeatureSpec with Checkers {
269269
assert(json == Json.obj("type" -> "Bar", "__syntheticWrap__" -> JsNumber(42)))
270270
assert(fooFormat.reads(json).asEither == Right(foo))
271271
}
272+
273+
import TestHelpers._
274+
val adt = Z(X(1), Y("VVV"))
275+
276+
Scenario("supports user-defined recursive formats - nested") {
277+
val adtFormat: Format[ADTBase] = {
278+
implicit val f1: Format[X] = Json.format
279+
implicit val f2: Format[Y] = Json.format
280+
implicit lazy val f3: Format[Z] = Json.format
281+
282+
implicit lazy val f4: Format[ADTBase] = oformat[ADTBase]()
283+
284+
f4
285+
}
286+
287+
val json = adtFormat.writes(adt)
288+
val obj = Json.obj(
289+
"Z" -> Json.obj(
290+
"l" -> Json.obj("X" -> Json.obj("a" -> 1)),
291+
"r" -> Json.obj("Y" -> Json.obj("b" -> "VVV"))))
292+
293+
assert(json == obj)
294+
assert(adtFormat.reads(json).asEither == Right(adt))
295+
}
296+
297+
Scenario("supports user-defined recursive formats - flat") {
298+
val adtFormat: Format[ADTBase] = {
299+
implicit val f1: Format[X] = Json.format
300+
implicit val f2: Format[Y] = Json.format
301+
implicit lazy val f3: Format[Z] = Json.format
302+
303+
implicit lazy val f4: Format[ADTBase] = flat.oformat((__ \ "type").format[String])
304+
305+
f4
306+
}
307+
308+
val json = adtFormat.writes(adt)
309+
val obj = Json.obj(
310+
"type" -> "Z",
311+
"l" -> Json.obj("type" -> "X", "a" -> 1),
312+
"r" -> Json.obj("type" -> "Y", "b" -> "VVV"))
313+
314+
assert(json == obj)
315+
assert(adtFormat.reads(json).asEither == Right(adt))
316+
}
272317
}
273318
}
319+
320+
321+
object TestHelpers {
322+
// Placing it here in a separate object since otherwise the Json.format macro fails to compile
323+
// for these types
324+
sealed trait ADTBase
325+
326+
case class X(a: Int) extends ADTBase
327+
case class Y(b: String) extends ADTBase
328+
case class Z(l: ADTBase, r: ADTBase) extends ADTBase
329+
}

0 commit comments

Comments
 (0)