4
votes

I have a json array of settings like so:

[
  {
    "name": "Company Name",
    "key": "company_name",
    "default": "Foo"
  }, {
    "name": "Deposit Weeks",
    "key": "deposit_weeks",
    "default": 6
  }, {
    "name": "Is VAT registered",
    "key": "vat_registered",
    "default": false
  }
]

I want to parse this into a Seq of Setting objects. I have tried to define my json format by using a trait and defining the different case classes according to the data type in the json object:

sealed trait Setting
case class StringSetting(name: String, key: String, default: String) extends Setting
case class IntSetting(name: String, key: String, default: Int) extends Setting
case class BoolSetting(name: String, key: String, default: Boolean) extends Setting

Now I try to parse the json:

val json = Json.parse(jsonStr)
implicit val jsonFormat: Format[Setting] = Json.format[Setting]

val result = Try(json.as[Seq[Setting]])

Here I get a compile error:

Error:(19, 61) No unapply or unapplySeq function found implicit val jsonFormat: Format[Setting] = Json.format[Setting]

Is there a way to map each setting to its appropriate case class?

1
Look at my code over here stackoverflow.com/a/49934623/297113Jai Prakash
@JaiPrakash your answer is not relevant to this question.asds_asds

1 Answers

4
votes
  1. The naive approach would be to provide Reads[Setting](if your aim just to convert json to object) so that JSON deserializer able to build the right variant of Setting.

    import play.api.libs.json._
    import play.api.libs.functional.syntax._
    
    implicit val settingReads: Reads[Setting] = (__ \ "default").read[String].map[Setting](StringSetting) |
                                        (__ \ "default").read[Int].map[Setting](IntSetting) |
                                        (__ \ "default").read[Boolean].map[Setting](BoolSetting)
    

However, this would not work if you have same type for 'default' in different sub classes. In this case JSON deserializer unable to distinguish between those two case classes.

  1. Another approach is to use play json variant library.

    import julienrf.variants.Variants
    
    sealed trait Setting
    case class StringSetting(name: String, key: String, default: String) extends Setting
    case class IntSetting(name: String, key: String, default: Int) extends Setting
    case class BoolSetting(name: String, key: String, default: Boolean) extends Setting
    
    object Setting {
      implicit val format: Format[Setting] = Variants.format[Setting] 
    }
    

    Variant.format provides both read and writes for Setting. Make sure that assignment of 'implicit val format' should happen after all possible subclass has been declared.

For more information regarding play json variant library click here