-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a compile-time option to toggle serialization of enum values and …
…sealed trait's case objects as JSON strings or JSON objects
- Loading branch information
1 parent
80adab8
commit 722f97c
Showing
15 changed files
with
718 additions
and
314 deletions.
There are no files selected for viewing
188 changes: 188 additions & 0 deletions
188
zio-json/shared/src/main/scala-2.x/zio/json/JsonCodecConfiguration.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package zio.json | ||
|
||
import zio.json.JsonCodecConfiguration.SumTypeHandling | ||
import zio.json.JsonCodecConfiguration.SumTypeHandling.WrapperWithClassNameField | ||
|
||
/** | ||
* When disabled for decoding, keys with empty collections will be omitted from the JSON. When disabled for encoding, | ||
* missing keys will default to empty collections. | ||
*/ | ||
case class ExplicitEmptyCollections(encoding: Boolean = true, decoding: Boolean = true) | ||
|
||
/** | ||
* Implicit codec derivation configuration. | ||
* | ||
* @param sumTypeHandling | ||
* see [[jsonDiscriminator]] | ||
* @param fieldNameMapping | ||
* see [[jsonMemberNames]] | ||
* @param allowExtraFields | ||
* see [[jsonNoExtraFields]] | ||
* @param sumTypeMapping | ||
* see [[jsonHintNames]] | ||
* @param explicitNulls | ||
* turns on explicit serialization of optional fields with None values | ||
* @param explicitEmptyCollections | ||
* turns on explicit serialization of fields with empty collections | ||
* @param enumValuesAsStrings | ||
* turns on serialization of enum values and sealed trait's case objects as strings | ||
*/ | ||
final case class JsonCodecConfiguration( | ||
sumTypeHandling: SumTypeHandling = WrapperWithClassNameField, | ||
fieldNameMapping: JsonMemberFormat = IdentityFormat, | ||
allowExtraFields: Boolean = true, | ||
sumTypeMapping: JsonMemberFormat = IdentityFormat, | ||
explicitNulls: Boolean = false, | ||
explicitEmptyCollections: ExplicitEmptyCollections = ExplicitEmptyCollections(), | ||
enumValuesAsStrings: Boolean = false | ||
) { | ||
def this( | ||
sumTypeHandling: SumTypeHandling, | ||
fieldNameMapping: JsonMemberFormat, | ||
allowExtraFields: Boolean, | ||
sumTypeMapping: JsonMemberFormat, | ||
explicitNulls: Boolean, | ||
explicitEmptyCollections: ExplicitEmptyCollections | ||
) = this( | ||
sumTypeHandling, | ||
fieldNameMapping, | ||
allowExtraFields, | ||
sumTypeMapping, | ||
explicitNulls, | ||
explicitEmptyCollections, | ||
false | ||
) | ||
|
||
def this( | ||
sumTypeHandling: SumTypeHandling, | ||
fieldNameMapping: JsonMemberFormat, | ||
allowExtraFields: Boolean, | ||
sumTypeMapping: JsonMemberFormat, | ||
explicitNulls: Boolean | ||
) = this( | ||
sumTypeHandling, | ||
fieldNameMapping, | ||
allowExtraFields, | ||
sumTypeMapping, | ||
explicitNulls, | ||
ExplicitEmptyCollections(), | ||
false | ||
) | ||
|
||
def copy( | ||
sumTypeHandling: SumTypeHandling = WrapperWithClassNameField.asInstanceOf[SumTypeHandling], | ||
fieldNameMapping: JsonMemberFormat = IdentityFormat.asInstanceOf[JsonMemberFormat], | ||
allowExtraFields: Boolean = true, | ||
sumTypeMapping: JsonMemberFormat = IdentityFormat.asInstanceOf[JsonMemberFormat], | ||
explicitNulls: Boolean = false, | ||
explicitEmptyCollections: ExplicitEmptyCollections = ExplicitEmptyCollections(), | ||
enumValuesAsStrings: Boolean = false | ||
) = new JsonCodecConfiguration( | ||
sumTypeHandling, | ||
fieldNameMapping, | ||
allowExtraFields, | ||
sumTypeMapping, | ||
explicitNulls, | ||
explicitEmptyCollections, | ||
enumValuesAsStrings | ||
) | ||
|
||
def copy( | ||
sumTypeHandling: SumTypeHandling, | ||
fieldNameMapping: JsonMemberFormat, | ||
allowExtraFields: Boolean, | ||
sumTypeMapping: JsonMemberFormat, | ||
explicitNulls: Boolean, | ||
explicitEmptyCollections: ExplicitEmptyCollections | ||
) = new JsonCodecConfiguration( | ||
sumTypeHandling, | ||
fieldNameMapping, | ||
allowExtraFields, | ||
sumTypeMapping, | ||
explicitNulls, | ||
explicitEmptyCollections, | ||
this.enumValuesAsStrings | ||
) | ||
|
||
def copy( | ||
sumTypeHandling: SumTypeHandling, | ||
fieldNameMapping: JsonMemberFormat, | ||
allowExtraFields: Boolean, | ||
sumTypeMapping: JsonMemberFormat, | ||
explicitNulls: Boolean | ||
) = new JsonCodecConfiguration( | ||
sumTypeHandling, | ||
fieldNameMapping, | ||
allowExtraFields, | ||
sumTypeMapping, | ||
explicitNulls, | ||
this.explicitEmptyCollections, | ||
this.enumValuesAsStrings | ||
) | ||
} | ||
|
||
object JsonCodecConfiguration { | ||
def apply( | ||
sumTypeHandling: SumTypeHandling, | ||
fieldNameMapping: JsonMemberFormat, | ||
allowExtraFields: Boolean, | ||
sumTypeMapping: JsonMemberFormat, | ||
explicitNulls: Boolean, | ||
explicitEmptyCollections: ExplicitEmptyCollections | ||
) = new JsonCodecConfiguration( | ||
sumTypeHandling, | ||
fieldNameMapping, | ||
allowExtraFields, | ||
sumTypeMapping, | ||
explicitNulls, | ||
explicitEmptyCollections, | ||
false | ||
) | ||
|
||
def apply( | ||
sumTypeHandling: SumTypeHandling, | ||
fieldNameMapping: JsonMemberFormat, | ||
allowExtraFields: Boolean, | ||
sumTypeMapping: JsonMemberFormat, | ||
explicitNulls: Boolean | ||
) = new JsonCodecConfiguration( | ||
sumTypeHandling, | ||
fieldNameMapping, | ||
allowExtraFields, | ||
sumTypeMapping, | ||
explicitNulls, | ||
ExplicitEmptyCollections(), | ||
false | ||
) | ||
|
||
implicit val default: JsonCodecConfiguration = JsonCodecConfiguration() | ||
|
||
sealed trait SumTypeHandling { | ||
def discriminatorField: Option[String] | ||
} | ||
|
||
object SumTypeHandling { | ||
|
||
/** | ||
* Use an object with a single key that is the class name. | ||
*/ | ||
case object WrapperWithClassNameField extends SumTypeHandling { | ||
override def discriminatorField: Option[String] = None | ||
} | ||
|
||
/** | ||
* For sealed classes, will determine the name of the field for disambiguating classes. | ||
* | ||
* The default is to not use a typehint field and instead have an object with a single key that is the class name. | ||
* See [[WrapperWithClassNameField]]. | ||
* | ||
* Note that using a discriminator is less performant, uses more memory, and may be prone to DOS attacks that are | ||
* impossible with the default encoding. In addition, there is slightly less type safety when using custom product | ||
* encoders (which must write an unenforced object type). Only use this option if you must model an externally | ||
* defined schema. | ||
*/ | ||
final case class DiscriminatorField(name: String) extends SumTypeHandling { | ||
override def discriminatorField: Option[String] = Some(name) | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.