BSON serialization with ADT and enumeration

98 views
Skip to first unread message

mikeb

unread,
Apr 19, 2021, 8:49:49 AM4/19/21
to ReactiveMongo - http://reactivemongo.org
Hello,

I have problem with BSON serialization with ADT structure and Enumeration library. 
Example structure:

sealed trait Color {
def value: String
}

case class UnknownColor(value : String) extends Color

sealed abstract class KnownColor(val value: String) extends StringEnumEntry with Color

object KnownColor extends StringEnum[KnownColor]{
override def values: IndexedSeq[KnownColor] = findValues

case object Red extends KnownColor("red")
case object Blue extends KnownColor("blue")
}

case class ColorWrapper(
id: UUID,
color: Color
)

I want to save in DB ColorWrapper. To do this I tried to create handlers like this:

implicit val knownColorFormat = new EnumHandler[KnownColor](KnownColor) //my own handler
implicit val unknownColorFormat = Macros.handler[UnknownColor]

type PredefinedColor = UnionType[KnownColor \/ UnknownColor] with Verbose
implicit val predefinedColor = Macros.handlerOpts[Color, PredefinedColor]
implicit val colorWrapperHandler = Macros.handler[ColorWrapper]

It compiles, but while testing writing ColorWrapper(UUID.randomUUID(), KnownColor.Red) I got runtime error:
reactivemongo.api.bson.exceptions.HandlerException, with message: Fails to handle 'color': Value doesn't match: Red.

From verbose I see that there is no KnownColor:

[info] /..../EnumTraitTest.scala:54:52: // Writer (predefinedColor)
[info] {
[info]   val macroCfg$macro$17: _root_.reactivemongo.api.bson.MacroConfiguration = bson.this.MacroConfiguration.default[reactivemongo.api.bson.MacroOptions](MacroOptions.this.ValueOf.optionsDefault);
[info]   macroVal match {
[info]     case (macroVal$macro$18 @ (_: EnumTraitTest.this.UnknownColor)) => EnumTraitTest.this.unknownColorFormat.writeTry(macroVal$macro$18).map(((x$1) => x$1.$plus$plus(_root_.reactivemongo.api.bson.BSONElement(macroCfg$macro$17.discriminator, _root_.reactivemongo.api.bson.BSONString(macroCfg$macro$17.typeNaming(implicitly[_root_.scala.reflect.ClassTag[EnumTraitTest.this.UnknownColor]].runtimeClass))))))
[info]     case _ => _root_.scala.util.Failure(_root_.reactivemongo.api.bson.exceptions.ValueDoesNotMatchException(macroVal.toString))
[info]   }
[info] }
[info]   implicit val predefinedColor = Macros.handlerOpts[Color, PredefinedColor]

Why predefinedColor does not contain KnownColor? Is there any other way to create macro to serialize it?

Cédric Chantepie

unread,
Apr 19, 2021, 5:24:31 PM4/19/21
to ReactiveMongo - http://reactivemongo.org
Which version? Which lib? Why not using enumeratum?

mikeb

unread,
Apr 20, 2021, 2:15:19 AM4/20/21
to ReactiveMongo - http://reactivemongo.org
Sorry, I forgot to add libs I'm using:
"org.reactivemongo" %% "reactivemongo" % "1.0.3",
"com.beachape" %% "enumeratum" % "1.6.1",

StringEnum and StringEnumEntry are from enumeratum. 

To avoid further misunderstanding, I created repository where you can reproduce issue https://github.com/michalbogacz/reactivemongo-playground/blob/master/src/test/scala/EnumTraitTest.scala 

Cédric Chantepie

unread,
Apr 20, 2021, 4:52:30 PM4/20/21
to ReactiveMongo - http://reactivemongo.org
There is an issue with macro options defined as type alias and not directly.
It can be fixed by either trying the 1.0.4-SNAPSHOT or just declaring the options directly:

  implicit val predefinedColor = Macros.handlerOpts[Color, UnionType[KnownColor \/ UnknownColor] with Verbose]

But then, there will a model design issue as your union type is mixing case class (represented as BSON document) and enum type (represented as simple BSON value), whereas all the member of a union type/sealed family need to be represented as document, so a discriminator can be added.

mikeb

unread,
Apr 22, 2021, 4:09:06 AM4/22/21
to ReactiveMongo - http://reactivemongo.org
Thank you for checking this, I was sure I was missing something. 
In the end, I decided to make my own handler, to be sure that serialization won't change with update of library.
Reply all
Reply to author
Forward
0 new messages