Problem serialising Enumeration to JSON

4,501 views
Skip to first unread message

Łukasz Lenart

unread,
Jun 25, 2013, 7:52:41 AM6/25/13
to spray...@googlegroups.com
Hi,

I have a problem serialising with Enumeration to JSON, some code:

object Currency extends Enumeration {

  type CurrencyType = Value
  val EUR = Value("EUR")

}

case class Transaction(transactionId: Option[TransactionId],
                       terminalId: TerminalId,
                       merchantId: MerchantId,
                       customerId: CustomerId,
                       amount: Double,
                       currency: Currency.CurrencyType,
                       timestamp: DateTime)

object Transaction {
  type TransactionId = String
}

and finally:

object DomainProtocol extends DefaultJsonProtocol {

  implicit val TransactionFormat = jsonFormat7(Transaction.apply)

}

and during compilation I'm getting:

error] CustomerService.scala:13: could not find implicit value for evidence parameter of type api.CustomerProtocol.JF[domain.Currency.CurrencyType]
[error]   implicit val TransactionFormat = jsonFormat7(Transaction.apply)
[error]                                               ^

What is wrong?

spray-can % 1.1-M7
spray-routing % 1.1-M7
spray-json % 1.2.5


Thanks in advance
--
Łukasz

Łukasz Lenart

unread,
Jun 25, 2013, 8:38:03 AM6/25/13
to spray...@googlegroups.com
Resolved, had to implement custom JsonFormat:

  implicit object CurrencyJsonFormat extends RootJsonFormat[Currency.CurrencyType] {
    def write(obj: Currency.CurrencyType): JsValue = JsString(obj.toString)

    def read(json: JsValue): Currency.CurrencyType = json match {
      case JsString(str) => Currency.withName(str)
      case _ => throw new DeserializationException("Enum string expected")
    }
  }
 


 

Johannes Rudolph

unread,
Jun 25, 2013, 8:44:46 AM6/25/13
to spray...@googlegroups.com
Hi Łukasz,

glad you figured it out. I guess we could provide something like that in spray-json directly. However, scala.Enumerations are not really widely used because of how many issues there are around them.


etc.

One little comment:

On Tue, Jun 25, 2013 at 2:38 PM, Łukasz Lenart <lukasz...@gmail.com> wrote:
Resolved, had to implement custom JsonFormat:

  implicit object CurrencyJsonFormat extends RootJsonFormat[Currency.CurrencyType] {

This should just be a `JsonFormat` instead of `RootJsonFormat`. `RootJsonFormat` is a designator that the JsonFormat produces only JsObjects or JsArrays which can be at the root of a JSON document.
 
--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

David Pérez

unread,
Jun 20, 2014, 5:26:13 AM6/20/14
to spray...@googlegroups.com, johannes...@googlemail.com
I've created this function in order to ease serialization of any enum:

def jsonEnum[T <: Enumeration](enu: T) = new JsonFormat[T#Value] {
def write(obj: T#Value) = JsString(obj.toString)

def read(json: JsValue) = json match {
case JsString(txt) => enu.withName(txt)
case something => throw new DeserializationException(s"Expected a value from enum $enu instead of $something")

Michael-Luk

unread,
Oct 28, 2014, 4:15:15 AM10/28/14
to spray...@googlegroups.com, johannes...@googlemail.com
Hi, David,

Could you pls show me how to use this function?


在 2014年6月20日星期五UTC+8下午5时26分13秒,David Pérez写道:

Craig Lenzen

unread,
Jun 18, 2015, 4:39:41 PM6/18/15
to spray...@googlegroups.com, johannes...@googlemail.com
Simple...

implicit val currencyFormat = jsonEnum(Currency)

Chris Bedford

unread,
Apr 21, 2016, 2:19:38 AM4/21/16
to spray.io User List, johannes...@googlemail.com
Hi, David - 
thanks for posting your code.  I converted your approach to use the spray.json that comes with Akka Http. I think they are basically the same.
Anyway.. here is a full usage example:



import com.ebay.krylov.conf.KrylovConf
import com.ebay.krylov.provisioner.common.Constants._
import spray.json.DefaultJsonProtocol._
import spray.json.{RootJsonFormat, _}

import scala.collection.immutable.Seq

import org.scalatest.{Matchers, WordSpec}
import spray.json.{JsString, JsValue, RootJsonFormat, _}


class EnumJsonConversionTest extends WordSpec with Matchers {

object Animal extends Enumeration {
type Animal = Value
val DOG, CAT, FISH, BIRD = Value
}

def jsonEnum[T <: Enumeration](enum: T) = new JsonFormat[T#Value] {

def write(obj: T#Value) = JsString(obj.toString)

def read(json: JsValue) = json match {
      case JsString(txt) => enum.withName(txt)
case something => throw new DeserializationException(s"Expected a value from enum $enum instead of $something")
}
}

class EnumJsonConverter[T <: scala.Enumeration](enu: T) extends RootJsonFormat[T#Value] {

def write(obj: T#Value) = JsString(obj.toString)

def read(json: JsValue) = {
json match {
case JsString(txt) => enu.withName(txt)
        case something => deserializationError(s"Expected a value from enum $enu instead of $something")
}
}
}

"JSON convertible enums" should {

implicit val converter = new EnumJsonConverter(Animal)

"ensure that a list of all values enum class should contain all of its elements" in {
val vals: Animal.ValueSet = Animal.values
vals.contains(Animal.DOG) shouldBe true // we won't bother to check the rest.... DOG should suffice
}

"convert objects containing Animal enum components to Json, & parse that Json back into the original object" in {
import Animal._

case class AnimalMap(name: String, map: Map[Animal,String]) // template for objects w/ Animal enum components
implicit val animalMapConverter = jsonFormat2(AnimalMap)

val mapAsJsonString = """{"name":"dogmap","map":{"DOG":"good dog!","CAT":"bad cat!"}}"""
val originalAnimalMap = AnimalMap("dogmap", Map(Animal.DOG -> "good dog!", Animal.CAT -> "bad cat!"))

val originalMapConvertedToJson: JsValue = originalAnimalMap.toJson

val parsedJson = mapAsJsonString.parseJson
System.out.println("parsedJson:" + parsedJson);
val jsonConvertedBackToAnimalMap: AnimalMap = parsedJson.convertTo[AnimalMap]
jsonConvertedBackToAnimalMap shouldEqual originalAnimalMap
}
}
}



Reply all
Reply to author
Forward
0 new messages