0

I have a requirement where the incoming JSON object is complex and mostly nested ex:

"users": {
  "utype": "PERSON",
  "language":"en_FR",
  "credentials": [
    {          
      "handle": "xyz@abc.com",
      "password": "123456",
      "handle_type": "EMAIL"
    }
  ],
  "person_details": {
    "primary": "true",
    "names": [
      {
      "name_type": "OFFICIAL",
      "title": "MR",
      "given": "abc",
      "family": "zat",
      "middle": "pqs",
      "suffix":"anathan"
     }
    ],
    "addresses": [
      {
        "ad_type": "HOME",
        "line1": "Residential 2211 North 1st Street",
        "line2": "Bldg 17",
        "city": "test",
        "county": "Shefield",
        "state" : "NY",
        "country_code": "xx",
        "postal_code": "95131"
      }
    ]
  }
}

For parsing this structure I use the below Case Classes

case class PersonUser (
    user_type:String,
    language_code:String,
    credentials:List[Credential],
    person_details:PersonDetails
)

case class Credential(handle:String, password:String,handle_type:String)

case class PersonDetails(
    primary_user:Boolean,
    names: List[Name],
    addresses:List[Address]
)

case class Name(
    name_type: String,
    title: String,
    given: String,
    family: String,
    middle: String,
    suffix:String
)

case class Address(
    address_type: String,
    line1: String,
    line2: String,
    city: String,
    county: String,
    state : String,
    country_code: String,
    postal_code: String
)

To convert the JSON structure to Scala I used JSON Inception:

 implicit val testReads = Json.reads[PersonUser]

Also I had to specify similar reads implicits in the sub classes - Credential, PersonDetails, Name and Address. Given below on such instance:

case class Credential(handle:String, password:String,handle_type:String)
object Credential{
   implicit val reads = Json.reads[Credential]
}

Now comes the question, if my JSON structure is really big with lots of sub-structures, there will be a number of Scala case classes I need to define. It will be really cumbersome to define companion objects and implicit read for each of the case classes (Ex: if I have 8 case classes to represent the JSON structure fully, I will have to define 8 more companion objects). Is there any way to avoid this extra work?

Anand
  • 601
  • 2
  • 7
  • 17
  • Nope, there isn't outside of rewriting the macro. But really, if you already define the case classes, is it really that much trouble to add one more line to each? – Michael Zajac Apr 13 '15 at 02:35
  • Yeah I dont think its a big deal but I felt the simplicity is lost a bit :-) – Anand Apr 13 '15 at 05:41

3 Answers3

2

This question is already answered but I thought I'd explain why it is the way it is.

  1. If the macro generated the necessary reads for nested case classes, this would make it impossible to define your own custom reads for those nested classes. The consequence, the macro would only be useful for the most trivial of cases, requiring switching to manual implementation of reads for the entire hierarchy just for the sake of defining custom behaviour for one deeply nested case class. The way the macro is implemented now, you can arbitrarily change any part of it easily.
  2. Yes, there is a small boilerplate overhead. But on the plus side, the compiler tells you if your structure can be deserialised, meaning you capture errors early, making refactoring safer, and also it means there's less implied magic that the only way you can know how it works is if you read the docs. By strongly typing everything, there is no magic, no surprises, no implied knowledge, and this leads to improved maintainability.
James Roper
  • 12,695
  • 46
  • 45
1

No, there is no way to avoid defining a Format instance for each class you want to (de)serialize.

Ryan
  • 7,227
  • 5
  • 29
  • 40
  • Yeah I agree with you that its is not boilerplate, but the moment it changes from "Defining Case Classes" to "Defining Case Classes + Companion Objects with implicit read defined" suddenly gives an un-easiness and felt Come on! it lots the atomicity (meaning. now I need to take care of more than one thing). But I must say that JSON inception is far better than the old way of mapping! – Anand Apr 13 '15 at 05:40
0

You can also have a look at other libs, at least Genson doesn't require you to define tons of implicits. The code you showed above should work with Genson by default.

import com.owlike.genson.defaultGenson_

val personUser: PersonUser = fromJson[PersonUser](json)
val json = toJson(personUser)

Genson has many other features, I'll let you judge by your self.

eugen
  • 5,856
  • 2
  • 29
  • 26