mardi 16 avril 2019

Fail-fast json4s serialisation of sealed trait and object enum when missing serializer

Set up

I have an enumeration defined using sealed trait, and a custom serializer for it:

import org.json4s.CustomSerializer
import org.json4s.JsonAST.JString
import org.json4s.DefaultFormats
import org.json4s.jackson.Serialization

sealed trait Foo
case object X extends Foo
case object Y extends Foo

object FooSerializer
    extends CustomSerializer[Foo](
      _ =>
        ({
          case JString("x") => X
          case JString("y") => Y
        }, {
          case X => JString("x")
          case Y => JString("y")
        })
    )

This is great, and works well when added to the formats:

{
  implicit val formats = DefaultFormats + FooSerializer
  Serialization.write(X) // "x"
}

This is great!

Problem

If the serializer is not added to the formats, json4s will use reflection to create a default representation of the fields, which is extremely unhelpful for these objects that don't have fields. It does this silently, seemingly without a way to control it.

{
  implicit val formats = DefaultFormats
  Serialization.write(X) // {}
}

This is a problematic, as there's no indication of what's gone wrong until much later. This invalid/useless data might be sent around the network or written to databases, if tests don't happen to catch it. And, this may be exposed publicly from a library, meaning downstream users have to remember it as well.

NB. this is different to read, which throws an exception on failure, since the Foo trait doesn't have any useful constructors:

{
  implicit val formats = DefaultFormats
  Serialization.read[Foo]("\"x\"")
}

org.json4s.package$MappingException: No constructor for type Foo, JString(x)
  at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$constructor(Extraction.scala:417)
  at org.json4s.Extraction$ClassInstanceBuilder.org$json4s$Extraction$ClassInstanceBuilder$$instantiate(Extraction.scala:468)
  at org.json4s.Extraction$ClassInstanceBuilder$$anonfun$result$6.apply(Extraction.scala:515)
...

Question

Is there a way to either disable the default {} formatting for these objects, or to "bake" in the formatting to the object itself?

For instance, having write throw an exception like read would be fine, as it would flag the problem to the caller immediately.





Aucun commentaire:

Enregistrer un commentaire