Skip to content

Instantly share code, notes, and snippets.

@holograph
Created December 18, 2012 15:22
Show Gist options
  • Save holograph/4328894 to your computer and use it in GitHub Desktop.
Save holograph/4328894 to your computer and use it in GitHub Desktop.
A workaround for the Lift-JSON bug documented here: https://github.com/lift/framework/issues/1080 Basically works by providing a single serializer for multiple enumerations, the order of which determines behavior in case of value collisions. Forked from a gist I accidentally created anonymously.
package com.tomergabel.examples
import net.liftweb.json._
import net.liftweb.json.JsonDSL._
/**
* A chained object which provides JSON serialization/deserialization facilities for multiple enumerations.
* Intended as a workaround for the following issue: https://github.com/lift/framework/issues/1080
*
* To use simply add to your formats: `implicit val formats = DefaultFormats + new ChainedEnumSerializer( enum1, enum2 )`
*
* See ChainedEnumSerializerTests for additional usage examples.
*/
class ChainedEnumSerializer( enums: Enumeration* ) extends Serializer[ Enumeration#Value ] {
private val predicate = classOf[ Enumeration#Value ]
private def throwOn( value: JValue ) =
throw new MappingException( "Can't convert %s to any of (%s)".format( value, enums.mkString( ", " ) ) )
def deserialize( implicit format: Formats ): PartialFunction[ ( TypeInfo, JValue ), Enumeration#Value ] = {
case ( TypeInfo( `predicate`, _ ), json ) => json match {
case wrapped @ JString( value ) =>
enums.flatMap { _.values.find( _.toString == value ) }.headOption getOrElse throwOn( wrapped )
case value => throwOn( value )
}
}
def serialize(implicit format: Formats): PartialFunction[Any, JValue] = {
case i: Enumeration#Value => i.toString
}
}
package com.tomergabel.examples
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.FlatSpec
/**
* Created by tomer on 12/18/12.
*/
class ChainedEnumSerializerTests extends FlatSpec with ShouldMatchers {
import ChainedEnumSerializerTests._
import net.liftweb.json._
implicit val formats = DefaultFormats + new ChainedEnumSerializer( Enum1, Enum2 )
"ChainedEnumSerializer" should "return correct enumeration value from the first enumeration" in {
val json = parse( """{"key1":"value1"}""" )
val result = json.extract[ TestObject ]
result should be === TestObject( key1 = Some( Enum1.value1 ) )
}
it should "correctly fall back to a secondary enumeraiton if a value does not match the first enumeration" in {
val json = parse( """{"key2":"value2"}""" )
val result = json.extract[ TestObject ]
result should be === TestObject( key2 = Some( Enum2.value2 ) )
}
it should "select a value from the first enumeration in case of collision between enumeration values" in {
val json = parse( """{"key1":"collision"}""" )
val result = json.extract[ TestObject ]
result should be === TestObject( key1 = Some( Enum1.collision ) )
}
it should "throw a MappingException in case of an invalid value accross all enumerations" in {
val json = parse( """{"m":"invalid"}""" )
evaluating { json.extract[ TestObject2 ] } should produce[ MappingException ]
}
}
private object ChainedEnumSerializerTests {
object Enum1 extends Enumeration { val value1, collision = Value }
object Enum2 extends Enumeration { val value2, collision = Value }
case class TestObject( key1: Option[ Enum1.Value ] = None, key2: Option[ Enum2.Value ] = None )
case class TestObject2( m: Enum1.Value )
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment