Skip to content

Commit 9c98fbe

Browse files
committed
Fix missing enforced discriminator field for module or enum classes
1 parent 14e317e commit 9c98fbe

File tree

3 files changed

+44
-33
lines changed

3 files changed

+44
-33
lines changed

jsoniter-scala-macros/shared/src/main/scala-2/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1807,12 +1807,6 @@ object JsonCodecMaker {
18071807
val l = in.readStringAsCharBuf()
18081808
${genReadEnumValue(javaEnumValues(tpe), q"in.enumValueError(l)")}
18091809
} else in.readNullOrTokenError(default, '"')"""
1810-
} else if (tpe.typeSymbol.isModuleClass) withDecoderFor(methodKey, default) {
1811-
q"""if (in.isNextToken('{')) {
1812-
in.rollbackToken()
1813-
in.skip()
1814-
${tpe.typeSymbol.asClass.module}
1815-
} else in.readNullOrTokenError(default, '{')"""
18161810
} else if (isTuple(tpe)) withDecoderFor(methodKey, default) {
18171811
val indexedTypes = tpe.typeArgs
18181812
val readFields = indexedTypes.tail.foldLeft[Tree] {
@@ -1834,6 +1828,12 @@ object JsonCodecMaker {
18341828
if (in.isNextToken(']')) new $tpe(..$params)
18351829
else in.arrayEndError()
18361830
} else in.readNullOrTokenError(default, '[')"""
1831+
} else if (tpe.typeSymbol.isModuleClass) withDecoderFor(methodKey, default) {
1832+
q"""if (in.isNextToken('{')) {
1833+
in.rollbackToken()
1834+
in.skip()
1835+
${tpe.typeSymbol.asClass.module}
1836+
} else in.readNullOrTokenError(default, '{')"""
18371837
} else if (isSealedClass(tpe)) withDecoderFor(methodKey, default) {
18381838
val leafClasses = adtLeafClasses(tpe)
18391839
val discriminatorError = cfg.discriminatorFieldName
@@ -2169,10 +2169,6 @@ object JsonCodecMaker {
21692169
if (encodingRequired) q"out.writeVal(x.name)"
21702170
else q"out.writeNonEscapedAsciiVal(x.name)"
21712171
}
2172-
} else if (tpe.typeSymbol.isModuleClass) withEncoderFor(methodKey, m) {
2173-
q"""out.writeObjectStart()
2174-
..$discriminator
2175-
out.writeObjectEnd()"""
21762172
} else if (isTuple(tpe)) withEncoderFor(methodKey, m) {
21772173
val writeFields = tpe.typeArgs.map {
21782174
var i = 0
@@ -2183,6 +2179,10 @@ object JsonCodecMaker {
21832179
q"""out.writeArrayStart()
21842180
..$writeFields
21852181
out.writeArrayEnd()"""
2182+
} else if (tpe.typeSymbol.isModuleClass && !(cfg.alwaysEmitDiscriminator && hasSealedParent(tpe))) withEncoderFor(methodKey, m) {
2183+
q"""out.writeObjectStart()
2184+
..$discriminator
2185+
out.writeObjectEnd()"""
21862186
} else if (isSealedClass(tpe) || (cfg.alwaysEmitDiscriminator && hasSealedParent(tpe))) withEncoderFor(methodKey, m) {
21872187
def genWriteLeafClass(subTpe: Type, discriminator: Tree): Tree =
21882188
if (subTpe != tpe) genWriteVal(q"x", subTpe :: types, isStringified, discriminator)

jsoniter-scala-macros/shared/src/main/scala-3/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMaker.scala

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2597,22 +2597,6 @@ object JsonCodecMaker {
25972597
${genReadJavaEnumValue(javaEnumValues(tpe), '{ $in.enumValueError(l) }, in, 'l) }
25982598
} else $in.readNullOrTokenError($default, '"')
25992599
}
2600-
} else if (isEnumOrModuleValue(tpe)) withDecoderFor(methodKey, default, in) { (in, default) =>
2601-
'{
2602-
if ($in.isNextToken('{')) {
2603-
$in.rollbackToken()
2604-
$in.skip()
2605-
${Ref(tpe.termSymbol).asExprOf[T]}
2606-
} else $in.readNullOrTokenError($default, '{')
2607-
}
2608-
} else if (tpe =:= TypeRepr.of[None.type]) withDecoderFor(methodKey, default, in) { (in, default) =>
2609-
'{
2610-
if ($in.isNextToken('{')) {
2611-
$in.rollbackToken()
2612-
$in.skip()
2613-
${'{ None }.asExprOf[T]}
2614-
} else $in.readNullOrTokenError($default, '{')
2615-
}
26162600
} else if (isTuple(tpe)) withDecoderFor(methodKey, default, in) { (in, default) =>
26172601
val indexedTypes = typeArgs(tpe)
26182602
var i = 0
@@ -2641,6 +2625,22 @@ object JsonCodecMaker {
26412625
if ($in.isNextToken('[')) ${readCreateBlock.asExprOf[T]}
26422626
else $in.readNullOrTokenError($default, '[')
26432627
}
2628+
} else if (isEnumOrModuleValue(tpe)) withDecoderFor(methodKey, default, in) { (in, default) =>
2629+
'{
2630+
if ($in.isNextToken('{')) {
2631+
$in.rollbackToken()
2632+
$in.skip()
2633+
${Ref(tpe.termSymbol).asExprOf[T]}
2634+
} else $in.readNullOrTokenError($default, '{')
2635+
}
2636+
} else if (tpe =:= TypeRepr.of[None.type]) withDecoderFor(methodKey, default, in) { (in, default) =>
2637+
'{
2638+
if ($in.isNextToken('{')) {
2639+
$in.rollbackToken()
2640+
$in.skip()
2641+
${'{ None }.asExprOf[T]}
2642+
} else $in.readNullOrTokenError($default, '{')
2643+
}
26442644
} else if (isSealedClass(tpe)) withDecoderFor(methodKey, default, in) { (in, default) =>
26452645
genReadSealedClass(types, in, default, isStringified)
26462646
} else if (isNonAbstractScalaClass(tpe)) withDecoderFor(methodKey, default, in) { (in, default) =>
@@ -3058,12 +3058,6 @@ object JsonCodecMaker {
30583058
if (encodingRequired) '{ $out.writeVal(($tx.name: String)) }
30593059
else '{ $out.writeNonEscapedAsciiVal(($tx.name: String)) }
30603060
}
3061-
} else if (isEnumOrModuleValue(tpe) || tpe =:= TypeRepr.of[None.type]) withEncoderFor(methodKey, m, out) { (out, x) =>
3062-
'{
3063-
$out.writeObjectStart()
3064-
${optWriteDiscriminator.fold('{})(_.write(out))}
3065-
$out.writeObjectEnd()
3066-
}
30673061
} else if (isTuple(tpe)) withEncoderFor(methodKey, m, out) { (out, x) =>
30683062
var i = 0
30693063
val writeFields = typeArgs(tpe).map { te =>
@@ -3074,6 +3068,13 @@ object JsonCodecMaker {
30743068
}
30753069
if (writeFields.isEmpty) fail(s"Expected that ${tpe.show} should be an applied type")
30763070
Block('{ $out.writeArrayStart() }.asTerm :: writeFields, '{ $out.writeArrayEnd() }.asTerm).asExprOf[Unit]
3071+
} else if ((isEnumOrModuleValue(tpe) || tpe =:= TypeRepr.of[None.type]) &&
3072+
!(cfg.alwaysEmitDiscriminator && hasSealedParent(tpe))) withEncoderFor(methodKey, m, out) { (out, x) =>
3073+
'{
3074+
$out.writeObjectStart()
3075+
${optWriteDiscriminator.fold('{})(_.write(out))}
3076+
$out.writeObjectEnd()
3077+
}
30773078
} else if (isSealedClass(tpe) || (cfg.alwaysEmitDiscriminator && hasSealedParent(tpe))) withEncoderFor(methodKey, m, out) { (out, x) =>
30783079
def genWriteLeafClass(subTpe: TypeRepr, discriminator: Option[WriteDiscriminator], vx: Term): Expr[Unit] =
30793080
subTpe.asType match

jsoniter-scala-macros/shared/src/test/scala/com/github/plokhotnyuk/jsoniter_scala/macros/JsonCodecMakerSpec.scala

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2222,13 +2222,15 @@ class JsonCodecMakerSpec extends VerifyingSpec {
22222222
verifyDeserError(codecOfRequiredAfterOptionalFields, """{"f1":1,"f2":2}""",
22232223
"""missing required field "f4", offset: 0x0000000e""")
22242224
}
2225-
"serialize and deserialize ADTs using ASCII discriminator field & value" in {
2225+
"serialize and deserialize case class ADTs using discriminator" in {
22262226
verifySerDeser(codecOfADTList1, List(AAA(1), BBB(BigInt(1)), CCC(1, "VVV"), DDD),
22272227
"""[{"type":"AAA","a":1},{"type":"BBB","a":1},{"type":"CCC","a":1,"b":"VVV"},{"type":"DDD"}]""")
22282228
verifySerDeser(codecOfADTList2, List(AAA(1), BBB(BigInt(1)), CCC(1, "VVV"), DDD),
22292229
"""[{"AAA":{"a":1}},{"BBB":{"a":1}},{"CCC":{"a":1,"b":"VVV"}},"DDD"]""")
22302230
verifySerDeser(make[List[AdtBase]](CodecMakerConfig.withDiscriminatorFieldName(_root_.scala.Some("t"))),
22312231
List(CCC(2, "WWW"), CCC(1, "VVV")), """[{"t":"CCC","a":2,"b":"WWW"},{"t":"CCC","a":1,"b":"VVV"}]""")
2232+
}
2233+
"serialize and deserialize case object ADTs without discriminator" in {
22322234
verifySerDeser(makeWithoutDiscriminator[List[Weapon]], List(Weapon.Axe, Weapon.Sword), """["Axe","Sword"]""")
22332235
verifySerDeser(make[List[Weapon]](CodecMakerConfig.withDiscriminatorFieldName(_root_.scala.None)),
22342236
List(Weapon.Axe, Weapon.Sword), """["Axe","Sword"]""")
@@ -2239,6 +2241,14 @@ class JsonCodecMakerSpec extends VerifyingSpec {
22392241
verifySerDeser(make[CCC], CCC(1, "VVV"), """{"a":1,"b":"VVV"}""")
22402242
verifySerDeser(make[DDD.type], DDD, """{}""")
22412243
}
2244+
"serialize and deserialize product types with enforced discriminators even if their codecs are derived not from the base ADT type" in {
2245+
verifySerDeser(make[AAA](CodecMakerConfig.withAlwaysEmitDiscriminator(true)), AAA(1), """{"type":"AAA","a":1}""")
2246+
verifySerDeser(make[BBB](CodecMakerConfig.withAlwaysEmitDiscriminator(true)), BBB(BigInt(1)),
2247+
"""{"type":"BBB","a":1}""")
2248+
verifySerDeser(make[CCC](CodecMakerConfig.withAlwaysEmitDiscriminator(true)), CCC(1, "VVV"),
2249+
"""{"type":"CCC","a":1,"b":"VVV"}""")
2250+
verifySerDeser(make[DDD.type](CodecMakerConfig.withAlwaysEmitDiscriminator(true)), DDD, """{"type":"DDD"}""")
2251+
}
22422252
"deserialize ADTs with extra fields" in {
22432253
sealed trait Base
22442254

0 commit comments

Comments
 (0)