Wojciech Durczyński Wojciech Durczyński - 3 months ago 17
JSON Question

Which JSON serialization library would fit to the following case?

I've got following case:
I'd like to serialize Scala case classes that extend parent class with var of type java.util.UUID.
Serialization of this case classes should happen without any configuration of them - no annotations and definition of custom formats. Any serialization hints may be situated in parent class.

I tried sjson, but Reflection based serialization can't serialize UUID types and type based serialization forces me to define formats for every case class.
Which json serialization library would best fit this case?

Answer

Here's one solution with Lift JSON.

import java.util.UUID
import net.liftweb.json._
import net.liftweb.json.JsonAST._
import net.liftweb.json.JsonDSL._
import net.liftweb.json.Serialization._

sealed abstract class Parent {
  def uuid: UUID
}
case class Foo(uuid: UUID, name: String) extends Parent

object UUIDTest extends Application {
  implicit val formats =  Serialization.formats(NoTypeHints) + new UUIDSerializer

  val f = Foo(UUID.randomUUID, "foo")
  val ser = write(f)
  println(ser)
  val f2 = read[Foo](ser)
  assert(f == f2)

  // Special serializer for UUID type
  class UUIDSerializer extends Serializer[UUID] {
    private val Class = classOf[UUID]

    def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), UUID] = {
      case (TypeInfo(Class, _), json) => json match {
        case JObject(JField("mostSig", JInt(m)) :: JField("leastSig", JInt(l)) :: Nil) =>
          new UUID(m.longValue, l.longValue)
        case x => throw new MappingException("Can't convert " + x + " to UUID")
      }
    }

    def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
      case x: UUID =>
        ("mostSig" -> x.getMostSignificantBits) ~ ("leastSig" -> x.getLeastSignificantBits)
    }
  }
}

It prints:

{"uuid":{"mostSig":-8054689529719995935,"leastSig":-5722404370736228056},"name":"foo"}'

Another solution which uses a custom serializer for Parent type.

sealed abstract class Parent {
  var uuid: UUID = UUID.randomUUID
}
case class Foo(name: String) extends Parent

object UUIDTest extends Application {
  implicit val formats = 
    Serialization.formats(NoTypeHints) + new ParentSerializer

  val f = Foo("foo")
  val ser = write(f)
  println(ser)
  val f2 = read[Foo](ser)
  assert(f == f2)

  // Special serializer for Parent type
  class ParentSerializer extends Serializer[Parent] {
    def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Parent] = {
      case (t@TypeInfo(cl, _), json) if (classOf[Parent].isAssignableFrom(cl)) => 
        val x = Extraction.extract(json, t)(DefaultFormats).asInstanceOf[Parent]
        x.uuid = (for { 
          JField("mostSig", JInt(m))  <- json
          JField("leastSig", JInt(l)) <- json 
        } yield new UUID(m.longValue, l.longValue)).head
        x
    }

    def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
      case x: Parent =>
        Extraction.decompose(x)(DefaultFormats) ++ 
        JField("mostSig", x.uuid.getMostSignificantBits) ++ 
        JField("leastSig", x.uuid.getLeastSignificantBits)
    }
  }
}
Comments