0

I have the following use-case:

Each class that I'm serde using JSON4S have a field, named ID. This ID can be any type T <: Stringifiable, where Stringifiable requires your ID type to be hashed to a string. Stringifiables also have constructors that rebuilds them from a string.

I'd like to serde any Stringifiable, for example ComplexIdentifier to a JSON of ID: stringified_identifier. Serialization works nicely, but unfortunately during deserialization, JSON4S is not going to use the default constructor which has only 1 string constructor. It finds the constructor, but if the identifier has a signature of case class ComplexIdentifier(whatever: String), it tries to extract a whatever name from the JString(stringified_identifier). That fails, so MappingException is thrown internally.

Is there any way to teach JSON4S to use the default constructor without extracting the values like this? It would be so obvious to just use the value from the JString and construct the Stringifiable using that.

Thanks!

dtolnay
  • 9,621
  • 5
  • 41
  • 62
Dyin
  • 5,815
  • 8
  • 44
  • 69

1 Answers1

0

Use the applymethod in a Companion to overload the constructor for the ID classes with a String parameter. Then just use a custom serializer for all of your ID types

sealed abstract class Stringifiable {}

case class ComplexIdentifier(whatever: List[Long]) extends Stringifiable
case class SimpleIdentifier(whatever: Int) extends Stringifiable

//Overload the default constructor
object ComplexIdentifier {
  def apply(s: String):ComplexIdentifier = {
    ComplexIdentifier(s.split(",").map(_.toLong).toList)
  }
}

case class MyClass(id: ComplexIdentifier, value: String)

Then use a custom serializer:

case object ComplexIdentifierSerializer extends CustomSerializer[ComplexIdentifier] ( formats =>
({
    case JString(id) => ComplexIdentifier(id)
    case JNull => null
 },
 {
   case x: ComplexIdentifier => JString(x.whatever.mkString(","))
 }))

Finally, make sure to include the serializer in the implicit formats:

implicit val formats = DefaultFormats ++ List(ComplexIdentifierSerializer)

println(parse("""
   {
    "id": "1",
    "value": "big value"
    }
    """).extract[MyClass])

  val c = MyClass(ComplexIdentifier("123,456"), "super value")
  println(write(c))
Jon Anderson
  • 696
  • 4
  • 9