-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #633 from AVSystem/ignore-transient-default
Allow Output to ignore @transientDefault
- Loading branch information
Showing
9 changed files
with
401 additions
and
54 deletions.
There are no files selected for viewing
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
15 changes: 15 additions & 0 deletions
15
core/src/main/scala/com/avsystem/commons/serialization/IgnoreTransientDefaultMarker.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,15 @@ | ||
package com.avsystem.commons | ||
package serialization | ||
|
||
/** | ||
* Instructs [[GenCodec]] to <b>ignore</b> the [[transientDefault]] annotation when serializing a case class. | ||
* This ensures that even if a field's value is the same as its default, it will be <b>included</b> in the serialized | ||
* representation. Deserialization behavior remains <b>unchanged</b>. If a field is missing from the input, the default | ||
* value will be used as usual. | ||
* | ||
* This marker can be helpful when using the same model class in multiple contexts with different serialization | ||
* formats that have conflicting requirements for handling default values. | ||
* | ||
* @see [[CustomMarkersOutputWrapper]] for an easy way to add markers to existing [[Output]] implementations | ||
*/ | ||
object IgnoreTransientDefaultMarker extends CustomEventMarker[Unit] |
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
107 changes: 107 additions & 0 deletions
107
core/src/main/scala/com/avsystem/commons/serialization/customMarkerWrappers.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,107 @@ | ||
package com.avsystem.commons | ||
package serialization | ||
|
||
trait AcceptsAdditionalCustomMarkers extends AcceptsCustomEvents { | ||
|
||
protected def markers: Set[CustomEventMarker[?]] | ||
|
||
override def customEvent[T](marker: CustomEventMarker[T], event: T): Boolean = | ||
markers(marker) || super.customEvent(marker, event) | ||
} | ||
|
||
/** | ||
* [[Input]] implementation that adds additional markers [[CustomEventMarker]] to the provided [[Input]] instance | ||
*/ | ||
final class CustomMarkersInputWrapper private( | ||
override protected val wrapped: Input, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends InputWrapper with AcceptsAdditionalCustomMarkers { | ||
|
||
override def readList(): ListInput = | ||
new CustomMarkersInputWrapper.AdjustedListInput(super.readList(), markers) | ||
|
||
override def readObject(): ObjectInput = | ||
new CustomMarkersInputWrapper.AdjustedObjectInput(super.readObject(), markers) | ||
} | ||
object CustomMarkersInputWrapper { | ||
def apply(input: Input, markers: CustomEventMarker[?]*): CustomMarkersInputWrapper = | ||
CustomMarkersInputWrapper(input, markers.toSet) | ||
|
||
def apply(input: Input, markers: Set[CustomEventMarker[?]]): CustomMarkersInputWrapper = | ||
new CustomMarkersInputWrapper(input, markers) | ||
|
||
private final class AdjustedListInput( | ||
override protected val wrapped: ListInput, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends ListInputWrapper with AcceptsAdditionalCustomMarkers { | ||
override def nextElement(): Input = new CustomMarkersInputWrapper(super.nextElement(), markers) | ||
} | ||
|
||
private final class AdjustedFieldInput( | ||
override protected val wrapped: FieldInput, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends FieldInputWrapper with AcceptsAdditionalCustomMarkers { | ||
|
||
override def readList(): ListInput = new AdjustedListInput(super.readList(), markers) | ||
override def readObject(): ObjectInput = new AdjustedObjectInput(super.readObject(), markers) | ||
} | ||
|
||
private final class AdjustedObjectInput( | ||
override protected val wrapped: ObjectInput, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends ObjectInputWrapper with AcceptsAdditionalCustomMarkers { | ||
|
||
override def nextField(): FieldInput = new AdjustedFieldInput(super.nextField(), markers) | ||
override def peekField(name: String): Opt[FieldInput] = | ||
super.peekField(name).map(new AdjustedFieldInput(_, markers)) | ||
} | ||
} | ||
|
||
/** | ||
* [[Output]] implementation that adds additional markers [[CustomEventMarker]] to the provided [[Output]] instance | ||
*/ | ||
final class CustomMarkersOutputWrapper private( | ||
override protected val wrapped: Output, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends OutputWrapper with AcceptsAdditionalCustomMarkers { | ||
|
||
override def writeSimple(): SimpleOutput = | ||
new CustomMarkersOutputWrapper.AdjustedSimpleOutput(super.writeSimple(), markers) | ||
|
||
override def writeList(): ListOutput = | ||
new CustomMarkersOutputWrapper.AdjustedListOutput(super.writeList(), markers) | ||
|
||
override def writeObject(): ObjectOutput = | ||
new CustomMarkersOutputWrapper.AdjustedObjectOutput(super.writeObject(), markers) | ||
} | ||
|
||
object CustomMarkersOutputWrapper { | ||
def apply(output: Output, markers: CustomEventMarker[?]*): CustomMarkersOutputWrapper = | ||
CustomMarkersOutputWrapper(output, markers.toSet) | ||
|
||
def apply(output: Output, markers: Set[CustomEventMarker[?]]): CustomMarkersOutputWrapper = | ||
new CustomMarkersOutputWrapper(output, markers) | ||
|
||
private final class AdjustedSimpleOutput( | ||
override protected val wrapped: SimpleOutput, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends SimpleOutputWrapper with AcceptsAdditionalCustomMarkers | ||
|
||
private final class AdjustedListOutput( | ||
override protected val wrapped: ListOutput, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends ListOutputWrapper with AcceptsAdditionalCustomMarkers { | ||
|
||
override def writeElement(): Output = | ||
new CustomMarkersOutputWrapper(super.writeElement(), markers) | ||
} | ||
|
||
private final class AdjustedObjectOutput( | ||
override protected val wrapped: ObjectOutput, | ||
override protected val markers: Set[CustomEventMarker[?]], | ||
) extends ObjectOutputWrapper with AcceptsAdditionalCustomMarkers { | ||
|
||
override def writeField(key: String): Output = | ||
new CustomMarkersOutputWrapper(super.writeField(key), markers) | ||
} | ||
} |
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
75 changes: 75 additions & 0 deletions
75
.../src/test/scala/com/avsystem/commons/serialization/IgnoreTransientDefaultMarkerTest.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,75 @@ | ||
package com.avsystem.commons | ||
package serialization | ||
|
||
import com.avsystem.commons.serialization.CodecTestData.HasDefaults | ||
|
||
object IgnoreTransientDefaultMarkerTest { | ||
final case class NestedHasDefaults( | ||
@transientDefault flag: Boolean = false, | ||
obj: HasDefaults, | ||
list: Seq[HasDefaults], | ||
@transientDefault defaultObj: HasDefaults = HasDefaults(), | ||
) | ||
object NestedHasDefaults extends HasGenCodec[NestedHasDefaults] | ||
|
||
final case class HasOptParam( | ||
@transientDefault flag: Boolean = false, | ||
@optionalParam str: Opt[String] = Opt.Empty, | ||
) | ||
object HasOptParam extends HasGenCodec[HasOptParam] | ||
} | ||
|
||
class IgnoreTransientDefaultMarkerTest extends AbstractCodecTest { | ||
import IgnoreTransientDefaultMarkerTest.* | ||
|
||
override type Raw = Any | ||
|
||
def writeToOutput(write: Output => Unit): Any = { | ||
var result: Any = null | ||
write(CustomMarkersOutputWrapper(new SimpleValueOutput(v => result = v), IgnoreTransientDefaultMarker)) | ||
result | ||
} | ||
|
||
def createInput(raw: Any): Input = | ||
CustomMarkersInputWrapper(new SimpleValueInput(raw), IgnoreTransientDefaultMarker) | ||
|
||
test("write case class with default values") { | ||
testWrite(HasDefaults(str = "lol"), Map("str" -> "lol", "int" -> 42)) | ||
testWrite(HasDefaults(43, "lol"), Map("int" -> 43, "str" -> "lol")) | ||
testWrite(HasDefaults(str = null), Map("str" -> null, "int" -> 42)) | ||
testWrite(HasDefaults(str = "dafuq"), Map("str" -> "dafuq", "int" -> 42)) | ||
} | ||
|
||
//noinspection RedundantDefaultArgument | ||
test("read case class with default values") { | ||
testRead(Map("str" -> "lol", "int" -> 42), HasDefaults(str = "lol", int = 42)) | ||
testRead(Map("str" -> "lol"), HasDefaults(str = "lol", int = 42)) | ||
testRead(Map("int" -> 43, "str" -> "lol"), HasDefaults(int = 43, str = "lol")) | ||
testRead(Map("str" -> null, "int" -> 42), HasDefaults(str = null, int = 42)) | ||
testRead(Map("str" -> null), HasDefaults(str = null, int = 42)) | ||
testRead(Map(), HasDefaults(str = "dafuq", int = 42)) | ||
} | ||
|
||
test("write case class with opt values") { | ||
testWrite(HasOptParam(str = "lol".opt), Map("flag" -> false, "str" -> "lol")) | ||
testWrite(HasOptParam(), Map("flag" -> false)) | ||
} | ||
|
||
//noinspection RedundantDefaultArgument | ||
test("write nested case class with default values") { | ||
testWrite( | ||
value = NestedHasDefaults( | ||
flag = false, | ||
obj = HasDefaults(str = "lol"), | ||
list = Seq(HasDefaults(int = 43)), | ||
defaultObj = HasDefaults(), | ||
), | ||
expectedRepr = Map( | ||
"flag" -> false, | ||
"defaultObj" -> Map[String, Any]("str" -> "kek", "int" -> 42), | ||
"obj" -> Map[String, Any]("str" -> "lol", "int" -> 42), | ||
"list" -> List(Map[String, Any]("str" -> "kek", "int" -> 43)), | ||
), | ||
) | ||
} | ||
} |
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.