Skip to content

Commit 070a12a

Browse files
committed
Cleanup tests for union types having null value + fix backward binary compatibility
1 parent fbdd283 commit 070a12a

File tree

4 files changed

+47
-57
lines changed

4 files changed

+47
-57
lines changed

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

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ final class stringified extends StaticAnnotation
5656
* arrays or collections (turned on by default)
5757
* @param transientNone a flag that turns on skipping serialization of fields that have empty values of
5858
* options (turned on by default)
59-
* @param transientNull a flag that turns on skipping serialization of fields that have null values of
60-
* objects (turned off by default)
6159
* @param requireCollectionFields a flag that turn on checking of presence of collection fields and forces
6260
* serialization when they are empty
6361
* @param bigDecimalPrecision a precision in 'BigDecimal' values (34 by default that is a precision for decimal128,
@@ -103,6 +101,8 @@ final class stringified extends StaticAnnotation
103101
* when the codec derived is for an ADT leaf class and not the ADT base class. Note that
104102
* this flag has no effect on generated decoders -- that is this flag does NOT cause
105103
* decoders to start requiring the discriminator field when they are not strictly necessary
104+
* @param transientNull a flag that turns on skipping serialization of fields that have null values of
105+
* objects (turned off by default)
106106
*/
107107
class CodecMakerConfig private[macros] (
108108
val fieldNameMapper: NameMapper,
@@ -115,7 +115,6 @@ class CodecMakerConfig private[macros] (
115115
val transientDefault: Boolean,
116116
val transientEmpty: Boolean,
117117
val transientNone: Boolean,
118-
val transientNull: Boolean,
119118
val requireCollectionFields: Boolean,
120119
val bigDecimalPrecision: Int,
121120
val bigDecimalScaleLimit: Int,
@@ -135,7 +134,8 @@ class CodecMakerConfig private[macros] (
135134
val checkFieldDuplication: Boolean,
136135
val scalaTransientSupport: Boolean,
137136
val inlineOneValueClasses: Boolean,
138-
val alwaysEmitDiscriminator: Boolean) {
137+
val alwaysEmitDiscriminator: Boolean,
138+
val transientNull: Boolean) {
139139
def withFieldNameMapper(fieldNameMapper: PartialFunction[String, String]): CodecMakerConfig =
140140
copy(fieldNameMapper = fieldNameMapper)
141141

@@ -164,8 +164,6 @@ class CodecMakerConfig private[macros] (
164164

165165
def withTransientNone(transientNone: Boolean): CodecMakerConfig = copy(transientNone = transientNone)
166166

167-
def withTransientNull(transientNull: Boolean): CodecMakerConfig = copy(transientNull = transientNull)
168-
169167
def withRequireCollectionFields(requireCollectionFields: Boolean): CodecMakerConfig =
170168
copy(requireCollectionFields = requireCollectionFields)
171169

@@ -219,6 +217,8 @@ class CodecMakerConfig private[macros] (
219217
def withInlineOneValueClasses(inlineOneValueClasses: Boolean): CodecMakerConfig =
220218
copy(inlineOneValueClasses = inlineOneValueClasses)
221219

220+
def withTransientNull(transientNull: Boolean): CodecMakerConfig = copy(transientNull = transientNull)
221+
222222
private[this] def copy(fieldNameMapper: NameMapper = fieldNameMapper,
223223
javaEnumValueNameMapper: NameMapper = javaEnumValueNameMapper,
224224
adtLeafClassNameMapper: NameMapper = adtLeafClassNameMapper,
@@ -229,7 +229,6 @@ class CodecMakerConfig private[macros] (
229229
transientDefault: Boolean = transientDefault,
230230
transientEmpty: Boolean = transientEmpty,
231231
transientNone: Boolean = transientNone,
232-
transientNull: Boolean = transientNull,
233232
requireCollectionFields: Boolean = requireCollectionFields,
234233
bigDecimalPrecision: Int = bigDecimalPrecision,
235234
bigDecimalScaleLimit: Int = bigDecimalScaleLimit,
@@ -249,7 +248,8 @@ class CodecMakerConfig private[macros] (
249248
checkFieldDuplication: Boolean = checkFieldDuplication,
250249
scalaTransientSupport: Boolean = scalaTransientSupport,
251250
inlineOneValueClasses: Boolean = inlineOneValueClasses,
252-
alwaysEmitDiscriminator: Boolean = alwaysEmitDiscriminator): CodecMakerConfig =
251+
alwaysEmitDiscriminator: Boolean = alwaysEmitDiscriminator,
252+
transientNull: Boolean = transientNull): CodecMakerConfig =
253253
new CodecMakerConfig(
254254
fieldNameMapper = fieldNameMapper,
255255
javaEnumValueNameMapper = javaEnumValueNameMapper,
@@ -261,7 +261,6 @@ class CodecMakerConfig private[macros] (
261261
transientDefault = transientDefault,
262262
transientEmpty = transientEmpty,
263263
transientNone = transientNone,
264-
transientNull = transientNull,
265264
requireCollectionFields = requireCollectionFields,
266265
bigDecimalPrecision = bigDecimalPrecision,
267266
bigDecimalScaleLimit = bigDecimalScaleLimit,
@@ -281,7 +280,8 @@ class CodecMakerConfig private[macros] (
281280
checkFieldDuplication = checkFieldDuplication,
282281
scalaTransientSupport = scalaTransientSupport,
283282
inlineOneValueClasses = inlineOneValueClasses,
284-
alwaysEmitDiscriminator = alwaysEmitDiscriminator)
283+
alwaysEmitDiscriminator = alwaysEmitDiscriminator,
284+
transientNull = transientNull)
285285
}
286286

287287
object CodecMakerConfig extends CodecMakerConfig(
@@ -295,7 +295,6 @@ object CodecMakerConfig extends CodecMakerConfig(
295295
transientDefault = true,
296296
transientEmpty = true,
297297
transientNone = true,
298-
transientNull = false,
299298
requireCollectionFields = false,
300299
bigDecimalPrecision = 34,
301300
bigDecimalScaleLimit = 6178,
@@ -315,7 +314,8 @@ object CodecMakerConfig extends CodecMakerConfig(
315314
checkFieldDuplication = true,
316315
scalaTransientSupport = false,
317316
inlineOneValueClasses = false,
318-
alwaysEmitDiscriminator = false) {
317+
alwaysEmitDiscriminator = false,
318+
transientNull = false) {
319319

320320
/**
321321
* Use to enable printing of codec during compilation:
@@ -348,7 +348,6 @@ object CodecMakerConfig extends CodecMakerConfig(
348348
case '{ ($x: CodecMakerConfig).withTransientDefault($v) } => Some(x.valueOrAbort.withTransientDefault(v.valueOrAbort))
349349
case '{ ($x: CodecMakerConfig).withTransientEmpty($v) } => Some(x.valueOrAbort.withTransientEmpty(v.valueOrAbort))
350350
case '{ ($x: CodecMakerConfig).withTransientNone($v) } => Some(x.valueOrAbort.withTransientNone(v.valueOrAbort))
351-
case '{ ($x: CodecMakerConfig).withTransientNull($v) } => Some(x.valueOrAbort.withTransientNull(v.valueOrAbort))
352351
case '{ ($x: CodecMakerConfig).withRequireCollectionFields($v) } => Some(x.valueOrAbort.withRequireCollectionFields(v.valueOrAbort))
353352
case '{ ($x: CodecMakerConfig).withRequireDefaultFields($v) } => Some(x.valueOrAbort.withRequireDefaultFields(v.valueOrAbort))
354353
case '{ ($x: CodecMakerConfig).withScalaTransientSupport($v) } => Some(x.valueOrAbort.withScalaTransientSupport(v.valueOrAbort))
@@ -363,6 +362,7 @@ object CodecMakerConfig extends CodecMakerConfig(
363362
case '{ ($x: CodecMakerConfig).withSetMaxInsertNumber($v) } => Some(x.valueOrAbort.withSetMaxInsertNumber(v.valueOrAbort))
364363
case '{ ($x: CodecMakerConfig).withSkipNestedOptionValues($v) } => Some(x.valueOrAbort.withSkipNestedOptionValues(v.valueOrAbort))
365364
case '{ ($x: CodecMakerConfig).withCirceLikeObjectEncoding($v) } => Some(x.valueOrAbort.withCirceLikeObjectEncoding(v.valueOrAbort))
365+
case '{ ($x: CodecMakerConfig).withTransientNull($v) } => Some(x.valueOrAbort.withTransientNull(v.valueOrAbort))
366366
case other =>
367367
report.error(s"Can't interpret ${other.show} as a constant expression, tree=$other")
368368
None

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,29 @@ case class IArrays(aa: IArray[IArray[Int]], a: IArray[BigInt])
3030

3131
case class IArrayDefaults(aa: IArray[IArray[Int]] = IArray(IArray[Int](1)), a: IArray[BigInt] = IArray[BigInt](2))
3232

33+
case class NullableProperty(a: Int | Null | String)
34+
35+
given nullableValueCodec: JsonValueCodec[Int | Null | String] = new JsonValueCodec[Int | Null | String] {
36+
def decodeValue(in: JsonReader, default: Int | Null | String): Int | Null | String = {
37+
val t = in.nextToken()
38+
if (t != 'n') {
39+
in.rollbackToken()
40+
if (t == '"') in.readString(null)
41+
else in.readInt()
42+
} else {
43+
in.readNullOrError("", "expected 'Int | Null | String'")
44+
null
45+
}
46+
}
47+
48+
def encodeValue(x: Int | Null | String, out: JsonWriter): Unit =
49+
if (x == null) out.writeNull()
50+
else if (x.isInstanceOf[String]) out.writeVal(x.asInstanceOf[String])
51+
else out.writeVal(x.asInstanceOf[Int])
52+
53+
def nullValue: Int | Null | String = null
54+
}
55+
3356
class JsonCodecMakerNewTypeSpec extends VerifyingSpec {
3457
val codecOfIArrays = make[IArrays]
3558

@@ -287,6 +310,16 @@ class JsonCodecMakerNewTypeSpec extends VerifyingSpec {
287310
verifySerDeser(jsonCodec, arr("VVV", 1.2, true, obj("WWW" -> None, "XXX" -> 777)),
288311
"""["VVV",1.2,true,{"WWW":null,"XXX":777}]""")
289312
}
313+
"serialize and deserialize case class with union types having null value (default behavior)" in {
314+
verifySerDeser(make[List[NullableProperty]],
315+
List(NullableProperty(null), NullableProperty(1), NullableProperty("VVV")),
316+
"""[{"a":null},{"a":1},{"a":"VVV"}]""")
317+
}
318+
"serialize and deserialize case class with union types having null value (transient null behavior)" in {
319+
verifySerDeser(make[List[NullableProperty]](CodecMakerConfig.withTransientNull(true)),
320+
List(NullableProperty(null), NullableProperty(1), NullableProperty("VVV")),
321+
"""[{},{"a":1},{"a":"VVV"}]""")
322+
}
290323
"don't generate codecs for non-concrete ADTs with at least one free type parameter" in {
291324
assert(intercept[TestFailedException](assertCompiles {
292325
"""sealed trait TypeBase[T]

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

Lines changed: 0 additions & 43 deletions
This file was deleted.

version.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
ThisBuild / version := "2.36.9-SNAPSHOT"
1+
ThisBuild / version := "2.37.0-SNAPSHOT"

0 commit comments

Comments
 (0)