Liftweb JSON serialization and deserialization using Scala


We are working on a Scala project using Play framework. We had written the Serializers and Deserialzers for parsing the JSON data that contains different data types like ObjectId , Scala Enumeration etc. We are using net.liftweb.json for writing and parsing the JSON data.

We were having the JSON looks like as :

[{“id”:1,”school”:”Cambridge”,”year”:{“name”:”FirstYear”},”major”:”Science”,”degree”:{“name”:”Bachelor’s”},”Date”:”01/05/2011″}]

The year field here is an scala enumeration and the Date has to be parsed as java.util.Date. We were end up with the following error for the enumeration:

play.core.ActionInvoker$$anonfun$receive$1$$anon$1: Execution exception [[MappingException: No usable value for year
No usable value for $outer
Parsed JSON values do not match with class constructor
args=
arg types=
constructor=public scala.Enumeration()]]
	at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:82) [play_2.9.1.jar:2.0]
	at play.core.ActionInvoker$$anonfun$receive$1.apply(Invoker.scala:63) [play_2.9.1.jar:2.0]
	at akka.actor.Actor$class.apply(Actor.scala:290) [akka-actor.jar:2.0]
	at play.core.ActionInvoker.apply(Invoker.scala:61) [play_2.9.1.jar:2.0]

We had written the customized serializers and deserializers for different data types. Here i am going to elaborate some of the important ones.

Enumeration Serializer and Deserializer

/*
*  Serializer and Deserializers for Enumeration
*/
class EnumerationSerializer(enumList: List[Enumeration]) extends net.liftweb.json.Serializer[Enumeration#Value] {
  import JsonDSL._
  val EnumerationClass = classOf[Enumeration#Value]
  val formats = Serialization.formats(NoTypeHints)

 //  Deserializer Function for Enumeration
  def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Enumeration#Value] = { 
    case (TypeInfo(EnumerationClass, _), json) => json match {
      case JObject(List(JField(name, JString(value)))) => fetchEnumValue(enumList, value)  // Here we can perform the
      case JString(value) => fetchEnumValue(enumList, value)                               // desired function
      case value => throw new MappingException("Can't convert " +
        value + " to " + EnumerationClass)
    }
  }

  def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
    case i: Enumeration#Value => i.toString
  }

  private def fetchEnumValue(enumList: List[Enumeration], value: String): Enumeration#Value = {
    var defaultEnumValue: Enumeration#Value = Year.withName("FinalYear")
    for (enumItem <- enumList) {
      for (enumValue <- enumItem.values) {
        enumValue.toString == value match {
          case true => {
            defaultEnumValue = enumItem.withName(value)
          }
          case _ => None
        }
      }
    }
    defaultEnumValue
  }

}

DateTime Serializers and Deserializer

/*
*  Serializer and Deserializers for Date
*/
object DateTimeSerializer extends Serializer[Option[Date]] {
  
  val formatter: DateFormat = new java.text.SimpleDateFormat("dd/MM/yyyy")
  
  private val MyDateClass = classOf[Option[Date]]

 //  Deserializer Function for java.util.Date
  def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Option[Date]] = {
    case (TypeInfo(MyDateClass, _), json) => json match {
      case JString(s) => {
        Option(formatter.parse(s))
      }

      case x => throw new MappingException("Can't convert " + x + " to Date")
    }
  
  }

  def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
    case x: Date => JObject(JField("graduationDate", JString(x.toString)) :: Nil)
  }
}

ObjectID Serializers and Deserializer

/*
*  Serializer and Deserializers for ObjectId
*/
class ObjectIdSerializer extends Serializer[ObjectId] {
  private val Class = classOf[ObjectId]

 //  Deserializer Function for ObjectId
  def deserialize(implicit format: Formats) = {
    case (TypeInfo(Class, _), json) => json match {
      case JInt(s) =>  new ObjectId
      case x => throw new MappingException("Can't convert " + x + " to ObjectId")
    }
  }

  def serialize(implicit format: Formats) = {
    case x: ObjectId => JObject(JField("schoolId", JString(x.toString)) :: Nil)
  }
}

We can parse the date by overriding the dateFormatter method as well.

 implicit val formats = new net.liftweb.json.DefaultFormats {
    override def dateFormatter = new SimpleDateFormat("dd/MM/yyyy")
  }

About Neelkanth Sachdeva

Project Manager @ eCIFM Solutions Inc. Previous : Senior Software Engineer @ Evernym Inc. Previous : Software Consultant @ Knoldus Software LLP Software Consultant @ Inphina Technologies
This entry was posted in Agile, Java, Scala, Web and tagged , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s