@@ -56,6 +56,8 @@ final class stringified extends StaticAnnotation
56
56
* arrays or collections (turned on by default)
57
57
* @param transientNone a flag that turns on skipping serialization of fields that have empty values of
58
58
* 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)
59
61
* @param requireCollectionFields a flag that turn on checking of presence of collection fields and forces
60
62
* serialization when they are empty
61
63
* @param bigDecimalPrecision a precision in 'BigDecimal' values (34 by default that is a precision for decimal128,
@@ -113,6 +115,7 @@ class CodecMakerConfig private[macros] (
113
115
val transientDefault : Boolean ,
114
116
val transientEmpty : Boolean ,
115
117
val transientNone : Boolean ,
118
+ val transientNull : Boolean ,
116
119
val requireCollectionFields : Boolean ,
117
120
val bigDecimalPrecision : Int ,
118
121
val bigDecimalScaleLimit : Int ,
@@ -161,6 +164,8 @@ class CodecMakerConfig private[macros] (
161
164
162
165
def withTransientNone (transientNone : Boolean ): CodecMakerConfig = copy(transientNone = transientNone)
163
166
167
+ def withTransientNull (transientNull : Boolean ): CodecMakerConfig = copy(transientNull = transientNull)
168
+
164
169
def withRequireCollectionFields (requireCollectionFields : Boolean ): CodecMakerConfig =
165
170
copy(requireCollectionFields = requireCollectionFields)
166
171
@@ -224,6 +229,7 @@ class CodecMakerConfig private[macros] (
224
229
transientDefault : Boolean = transientDefault,
225
230
transientEmpty : Boolean = transientEmpty,
226
231
transientNone : Boolean = transientNone,
232
+ transientNull : Boolean = transientNull,
227
233
requireCollectionFields : Boolean = requireCollectionFields,
228
234
bigDecimalPrecision : Int = bigDecimalPrecision,
229
235
bigDecimalScaleLimit : Int = bigDecimalScaleLimit,
@@ -255,6 +261,7 @@ class CodecMakerConfig private[macros] (
255
261
transientDefault = transientDefault,
256
262
transientEmpty = transientEmpty,
257
263
transientNone = transientNone,
264
+ transientNull = transientNull,
258
265
requireCollectionFields = requireCollectionFields,
259
266
bigDecimalPrecision = bigDecimalPrecision,
260
267
bigDecimalScaleLimit = bigDecimalScaleLimit,
@@ -288,6 +295,7 @@ object CodecMakerConfig extends CodecMakerConfig(
288
295
transientDefault = true ,
289
296
transientEmpty = true ,
290
297
transientNone = true ,
298
+ transientNull = false ,
291
299
requireCollectionFields = false ,
292
300
bigDecimalPrecision = 34 ,
293
301
bigDecimalScaleLimit = 6178 ,
@@ -340,6 +348,7 @@ object CodecMakerConfig extends CodecMakerConfig(
340
348
case ' { ($x : CodecMakerConfig ).withTransientDefault($v) } => Some (x.valueOrAbort.withTransientDefault(v.valueOrAbort))
341
349
case ' { ($x : CodecMakerConfig ).withTransientEmpty($v) } => Some (x.valueOrAbort.withTransientEmpty(v.valueOrAbort))
342
350
case ' { ($x : CodecMakerConfig ).withTransientNone($v) } => Some (x.valueOrAbort.withTransientNone(v.valueOrAbort))
351
+ case ' { ($x : CodecMakerConfig ).withTransientNull($v) } => Some (x.valueOrAbort.withTransientNull(v.valueOrAbort))
343
352
case ' { ($x : CodecMakerConfig ).withRequireCollectionFields($v) } => Some (x.valueOrAbort.withRequireCollectionFields(v.valueOrAbort))
344
353
case ' { ($x : CodecMakerConfig ).withRequireDefaultFields($v) } => Some (x.valueOrAbort.withRequireDefaultFields(v.valueOrAbort))
345
354
case ' { ($x : CodecMakerConfig ).withScalaTransientSupport($v) } => Some (x.valueOrAbort.withScalaTransientSupport(v.valueOrAbort))
@@ -677,6 +686,7 @@ object JsonCodecMaker {
677
686
transientDefault = false ,
678
687
transientEmpty = false ,
679
688
transientNone = false ,
689
+ transientNull = true ,
680
690
requireCollectionFields = false ,
681
691
bigDecimalPrecision = 34 ,
682
692
bigDecimalScaleLimit = 6178 ,
@@ -794,6 +804,11 @@ object JsonCodecMaker {
794
804
def isOption (tpe : TypeRepr , types : List [TypeRepr ]): Boolean = tpe <:< TypeRepr .of[Option [_]] &&
795
805
(cfg.skipNestedOptionValues || ! types.headOption.exists(_ <:< TypeRepr .of[Option [_]]))
796
806
807
+ def isNullable (tpe : TypeRepr ): Boolean = tpe match {
808
+ case OrType (left, right) => isNullable(right) || isNullable(left)
809
+ case _ => tpe =:= TypeRepr .of[Null ]
810
+ }
811
+
797
812
def isIArray (tpe : TypeRepr ): Boolean = tpe.typeSymbol.fullName == " scala.IArray$package$.IArray"
798
813
799
814
def isCollection (tpe : TypeRepr ): Boolean = tpe <:< TypeRepr .of[Iterable [_]] || tpe <:< TypeRepr .of[Iterator [_]] ||
@@ -1945,7 +1960,7 @@ object JsonCodecMaker {
1945
1960
else mappedNames
1946
1961
})
1947
1962
val required = fields.collect {
1948
- case fieldInfo if ! ((! cfg.requireDefaultFields && fieldInfo.symbol.flags.is(Flags .HasDefault )) || isOption(fieldInfo.resolvedTpe, types) ||
1963
+ case fieldInfo if ! ((! cfg.requireDefaultFields && fieldInfo.symbol.flags.is(Flags .HasDefault )) || isOption(fieldInfo.resolvedTpe, types) || isNullable(fieldInfo.resolvedTpe) ||
1949
1964
(! cfg.requireCollectionFields && isCollection(fieldInfo.resolvedTpe))) => fieldInfo.mappedName
1950
1965
}.toSet
1951
1966
val paramVarNum = fields.size
@@ -2692,6 +2707,12 @@ object JsonCodecMaker {
2692
2707
$ {genWriteVal(' {v.get}, tpe1 :: fTpe :: types, fieldInfo.isStringified, None , out)}
2693
2708
}
2694
2709
}
2710
+ } else if (cfg.transientNull && isNullable(fTpe)) ' {
2711
+ val v = $ {Select (x.asTerm, fieldInfo.getterOrField).asExprOf[ft]}
2712
+ if ((v != null ) && v != $ {d.asExprOf[ft]}) {
2713
+ $ {genWriteConstantKey(fieldInfo.mappedName, out)}
2714
+ $ {genWriteVal(' v , fTpe :: types, fieldInfo.isStringified, None , out)}
2715
+ }
2695
2716
} else if (fTpe <:< TypeRepr .of[Array [_]]) {
2696
2717
def cond (v : Expr [Array [_]])(using Quotes ): Expr [Boolean ] =
2697
2718
val da = d.asExprOf[Array [_]]
@@ -2756,6 +2777,12 @@ object JsonCodecMaker {
2756
2777
$ {genWriteVal(' { v.get }, tpe1 :: fTpe :: types, fieldInfo.isStringified, None , out)}
2757
2778
}
2758
2779
}
2780
+ } else if (cfg.transientNull && isNullable(fTpe)) ' {
2781
+ val v = $ {Select (x.asTerm, fieldInfo.getterOrField).asExprOf[ft]}
2782
+ if (v != null ) {
2783
+ $ {genWriteConstantKey(fieldInfo.mappedName, out)}
2784
+ $ {genWriteVal(' v , fTpe :: types, fieldInfo.isStringified, None , out)}
2785
+ }
2759
2786
} else if (cfg.transientEmpty && fTpe <:< TypeRepr .of[Array [_]]) ' {
2760
2787
val v = $ {Select (x.asTerm, fieldInfo.getterOrField).asExprOf[ft & Array [_]]}
2761
2788
if (v.length != 0 ) {
0 commit comments