Skip to content

Commit

Permalink
revert feature excludeEmptyCollections, remove completely (#1236)
Browse files Browse the repository at this point in the history
  • Loading branch information
ThijsBroersen authored Jan 23, 2025
1 parent 6de0c92 commit cdbb36f
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 1,106 deletions.
2 changes: 1 addition & 1 deletion project/BuildHelper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ object BuildHelper {
autoAPIMappings := true,
unusedCompileDependenciesFilter -= moduleFilter("org.scala-js", "scalajs-library"),
mimaPreviousArtifacts := {
previousStableVersion.value.map(organization.value %% name.value % _).toSet ++
previousStableVersion.value.filter(_ != "0.7.4").map(organization.value %% name.value % _).toSet ++
Set(organization.value %% name.value % "0.7.3")
},
mimaCheckDirection := "backward", // TODO: find how we can use "both" for path versions
Expand Down
33 changes: 8 additions & 25 deletions zio-json/shared/src/main/scala-2.x/zio/json/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,10 @@ final case class jsonField(name: String) extends Annotation
*/
final case class jsonAliases(alias: String, aliases: String*) extends Annotation

final class jsonExplicitNull extends Annotation

/**
* When disabled keys with empty collections will be omitted from the JSON.
* Empty option fields will be encoded as `null`.
*/
final case class jsonExplicitEmptyCollection(enabled: Boolean = true) extends Annotation
final class jsonExplicitNull extends Annotation

/**
* If used on a sealed class, will determine the name of the field for disambiguating classes.
Expand Down Expand Up @@ -214,6 +212,7 @@ object DeriveJsonDecoder {

if (ctx.parameters.isEmpty)
new JsonDecoder[A] {

def unsafeDecode(trace: List[JsonError], in: RetractReader): A = {
if (no_extra) {
Lexer.char(trace, in, '{')
Expand Down Expand Up @@ -268,7 +267,7 @@ object DeriveJsonDecoder {
ctx.parameters.map(_.typeclass).toArray.asInstanceOf[Array[JsonDecoder[Any]]]
private[this] lazy val namesMap = (names.zipWithIndex ++ aliases).toMap

def unsafeDecode(trace: List[JsonError], in: RetractReader): A = {
override def unsafeDecode(trace: List[JsonError], in: RetractReader): A = {
Lexer.char(trace, in, '{')

// TODO it would be more efficient to have a solution that didn't box
Expand Down Expand Up @@ -433,6 +432,7 @@ object DeriveJsonEncoder {
def join[A](ctx: CaseClass[JsonEncoder, A])(implicit config: JsonCodecConfiguration): JsonEncoder[A] =
if (ctx.parameters.isEmpty)
new JsonEncoder[A] {

def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = out.write("{}")

override final def toJsonAST(a: A): Either[String, Json] =
Expand All @@ -457,28 +457,19 @@ object DeriveJsonEncoder {
}
private[this] val explicitNulls =
config.explicitNulls || ctx.annotations.exists(_.isInstanceOf[jsonExplicitNull])
private[this] val explicitEmptyCollections =
ctx.annotations.collectFirst { case jsonExplicitEmptyCollection(enabled) =>
enabled
}.getOrElse(config.explicitEmptyCollections)
private[this] lazy val fields = params.map {
var idx = 0
p =>
val field = (
p,
names(idx),
p.typeclass.asInstanceOf[JsonEncoder[Any]],
explicitNulls || p.annotations.exists(_.isInstanceOf[jsonExplicitNull]),
p.annotations.collectFirst { case jsonExplicitEmptyCollection(enabled) =>
enabled
}.getOrElse(explicitEmptyCollections)
explicitNulls || p.annotations.exists(_.isInstanceOf[jsonExplicitNull])
)
idx += 1
field
}

override def isEmpty(a: A): Boolean = params.forall(p => p.typeclass.isEmpty(p.dereference(a)))

def unsafeEncode(a: A, indent: Option[Int], out: Write): Unit = {
out.write('{')
val indent_ = JsonEncoder.bump(indent)
Expand All @@ -491,8 +482,7 @@ object DeriveJsonEncoder {
val p = field._1.dereference(a)
if ({
val isNothing = field._3.isNothing(p)
val isEmpty = field._3.isEmpty(p)
(!isNothing && !isEmpty) || (isNothing && field._4) || (isEmpty && field._5)
!isNothing || field._4
}) {
// if we have at least one field already, we need a comma
if (prevFields) {
Expand All @@ -518,16 +508,9 @@ object DeriveJsonEncoder {
name
}.getOrElse(nameTransform(param.label))
val writeNulls = explicitNulls || param.annotations.exists(_.isInstanceOf[jsonExplicitNull])
val writeEmptyCollections =
param.annotations.collectFirst { case jsonExplicitEmptyCollection(enabled) =>
enabled
}.getOrElse(explicitEmptyCollections)
c.flatMap { chunk =>
param.typeclass.toJsonAST(param.dereference(a)).map { value =>
if (
(value == Json.Null && !writeNulls) ||
(value.asObject.exists(_.fields.isEmpty) && !writeEmptyCollections)
) chunk
if (!writeNulls && value == Json.Null) chunk
else chunk :+ name -> value
}
}
Expand Down
34 changes: 10 additions & 24 deletions zio-json/shared/src/main/scala-3/zio/json/macros.scala
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,6 @@ final case class jsonAliases(alias: String, aliases: String*) extends Annotation
*/
final class jsonExplicitNull extends Annotation

/**
* When disabled keys with empty collections will be omitted from the JSON.
*/
final case class jsonExplicitEmptyCollection(enabled: Boolean = true) extends Annotation

/**
* If used on a sealed class, will determine the name of the field for
* disambiguating classes.
Expand Down Expand Up @@ -288,7 +283,7 @@ sealed class JsonDecoderDerivation(config: JsonCodecConfiguration) extends Deriv
IArray.genericWrapArray(ctx.params.map(_.typeclass)).toArray.asInstanceOf[Array[JsonDecoder[Any]]]
private lazy val namesMap = (names.zipWithIndex ++ aliases).toMap

def unsafeDecode(trace: List[JsonError], in: RetractReader): A = {
override def unsafeDecode(trace: List[JsonError], in: RetractReader): A = {
Lexer.char(trace, in, '{')
val ps = new Array[Any](len)
if (Lexer.firstField(trace, in))
Expand Down Expand Up @@ -470,6 +465,7 @@ sealed class JsonDecoderDerivation(config: JsonCodecConfiguration) extends Deriv
}

private lazy val caseObjectEncoder = new JsonEncoder[Any] {

def unsafeEncode(a: Any, indent: Option[Int], out: Write): Unit =
out.write("{}")

Expand Down Expand Up @@ -511,17 +507,15 @@ sealed class JsonEncoderDerivation(config: JsonCodecConfiguration) extends Deriv
}.getOrElse(if (transformNames) nameTransform(p.label) else p.label)
}.toArray
private val explicitNulls = config.explicitNulls || ctx.annotations.exists(_.isInstanceOf[jsonExplicitNull])
private val explicitEmptyCollections = ctx.annotations.collectFirst { case jsonExplicitEmptyCollection(enabled) =>
enabled
}.getOrElse(config.explicitEmptyCollections)
private lazy val fields = params.map {
var idx = 0
p =>
val field = (p, names(idx), p.typeclass.asInstanceOf[JsonEncoder[Any]],
explicitNulls || p.annotations.exists(_.isInstanceOf[jsonExplicitNull]),
p.annotations.collectFirst { case jsonExplicitEmptyCollection(enabled) =>
enabled
}.getOrElse(explicitEmptyCollections))
val field = (
p,
names(idx),
p.typeclass.asInstanceOf[JsonEncoder[Any]],
explicitNulls || p.annotations.exists(_.isInstanceOf[jsonExplicitNull])
)
idx += 1
field
}.toArray
Expand All @@ -538,8 +532,7 @@ sealed class JsonEncoderDerivation(config: JsonCodecConfiguration) extends Deriv
val p = field._1.deref(a)
if ({
val isNothing = field._3.isNothing(p)
val isEmpty = field._3.isEmpty(p)
(!isNothing && !isEmpty) || (isNothing && field._4) || (isEmpty && field._5)
!isNothing || field._4
}) {
// if we have at least one field already, we need a comma
if (prevFields) {
Expand All @@ -565,16 +558,9 @@ sealed class JsonEncoderDerivation(config: JsonCodecConfiguration) extends Deriv
name
}.getOrElse(nameTransform(param.label))
val writeNulls = explicitNulls || param.annotations.exists(_.isInstanceOf[jsonExplicitNull])
val writeEmptyCollections =
param.annotations.collectFirst { case jsonExplicitEmptyCollection(enabled) =>
enabled
}.getOrElse(explicitEmptyCollections)
c.flatMap { chunk =>
param.typeclass.toJsonAST(param.deref(a)).map { value =>
if (
(value == Json.Null && !writeNulls) ||
(value.asObject.exists(_.fields.isEmpty) && !writeEmptyCollections)
) chunk
if (!writeNulls && value == Json.Null) chunk
else chunk :+ name -> value
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,77 +14,33 @@ import zio.json.JsonCodecConfiguration.SumTypeHandling.WrapperWithClassNameField
* see [[jsonNoExtraFields]]
* @param sumTypeMapping
* see [[jsonHintNames]]
* @param explicitNulls
* see [[jsonExplicitNull]]
*/
final case class JsonCodecConfiguration(
sumTypeHandling: SumTypeHandling = WrapperWithClassNameField,
fieldNameMapping: JsonMemberFormat = IdentityFormat,
allowExtraFields: Boolean = true,
sumTypeMapping: JsonMemberFormat = IdentityFormat,
explicitNulls: Boolean = false,
explicitEmptyCollections: Boolean = true
explicitNulls: Boolean = false
) {
def this(
sumTypeHandling: SumTypeHandling,
fieldNameMapping: JsonMemberFormat,
allowExtraFields: Boolean,
sumTypeMapping: JsonMemberFormat,
explicitNulls: Boolean
) = this(
sumTypeHandling,
fieldNameMapping,
allowExtraFields,
sumTypeMapping,
explicitNulls,
true
)

def copy(
sumTypeHandling: SumTypeHandling = WrapperWithClassNameField.asInstanceOf[SumTypeHandling],
fieldNameMapping: JsonMemberFormat = IdentityFormat.asInstanceOf[JsonMemberFormat],
allowExtraFields: Boolean = true,
sumTypeMapping: JsonMemberFormat = IdentityFormat.asInstanceOf[JsonMemberFormat],
explicitNulls: Boolean = false,
explicitEmptyCollections: Boolean = true
explicitNulls: Boolean = false
) = new JsonCodecConfiguration(
sumTypeHandling,
fieldNameMapping,
allowExtraFields,
sumTypeMapping,
explicitNulls,
explicitEmptyCollections
)

def copy(
sumTypeHandling: SumTypeHandling,
fieldNameMapping: JsonMemberFormat,
allowExtraFields: Boolean,
sumTypeMapping: JsonMemberFormat,
explicitNulls: Boolean
) = new JsonCodecConfiguration(
sumTypeHandling,
fieldNameMapping,
allowExtraFields,
sumTypeMapping,
explicitNulls,
true
explicitNulls
)
}

object JsonCodecConfiguration {
def apply(
sumTypeHandling: SumTypeHandling,
fieldNameMapping: JsonMemberFormat,
allowExtraFields: Boolean,
sumTypeMapping: JsonMemberFormat,
explicitNulls: Boolean
) = new JsonCodecConfiguration(
sumTypeHandling,
fieldNameMapping,
allowExtraFields,
sumTypeMapping,
explicitNulls,
true
)

implicit val default: JsonCodecConfiguration = JsonCodecConfiguration()

Expand Down
Loading

0 comments on commit cdbb36f

Please sign in to comment.