{"action":"created","issue":{"url":"https://api.github.com/repos/propensive/rapture-json/issues/44","labels_url":"https://api.github.com/repos/propensive/rapture-json/issues/44/labels{/name}","comments_url":"https://api.github.com/repos/propensive/rapture-json/issues/44/comments","events_url":"https://api.github.com/repos/propensive/rapture-json/issues/44/events","html_url":"https://github.com/propensive/rapture-json/issues/44","id":62518323,"number":44,"title":"Deserialization/serialization for list of objects belonging to a class hierarchy","user":{"login":"landlockedsurfer","id":3172001,"avatar_url":"https://avatars.githubusercontent.com/u/3172001?v=3","gravatar_id":"","url":"https://api.github.com/users/landlockedsurfer","html_url":"https://github.com/landlockedsurfer","followers_url":"https://api.github.com/users/landlockedsurfer/followers","following_url":"https://api.github.com/users/landlockedsurfer/following{/other_user}","gists_url":"https://api.github.com/users/landlockedsurfer/gists{/gist_id}","starred_url":"https://api.github.com/users/landlockedsurfer/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/landlockedsurfer/subscriptions","organizations_url":"https://api.github.com/users/landlockedsurfer/orgs","repos_url":"https://api.github.com/users/landlockedsurfer/repos","events_url":"https://api.github.com/users/landlockedsurfer/events{/privacy}","received_events_url":"https://api.github.com/users/landlockedsurfer/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":1,"created_at":"2015-03-17T21:04:36Z","updated_at":"2015-03-22T17:04:27Z","closed_at":null,"body":"Hi Jon!\r\n\r\nRapture-Json really rocks! The way you managed to swap out the various JSON backends is mindboggling.\r\nIn a project of mine, I'm trying to parse a list of objects belonging to a class hierarchy. The solution I found below is working, but I'm sure there is a better way to achieve this in Rapture-Json.\r\nCan you give me some hints on how a more elegant implementation would look like?\r\n\r\nThanks,\r\nManfred\r\n\r\n```\r\npackage rapturejson.test\r\n\r\nimport org.scalatest.FunSuite\r\nimport rapture.json._\r\nimport rapture.json.jsonBackends.play._\r\n\r\nclass RaptureJsonSuite extends FunSuite {\r\n\r\n  test(\"parse class hierarchy\") {\r\n\r\n    // Simple class hierarchy of shapes\r\n    trait Shape\r\n    case class Circle(radius:Int) extends Shape\r\n    case class Square(size:Int) extends Shape\r\n\r\n    // Everything's working nicely for a list containing the same shapes\r\n    val circles = List[Circle](Circle(1),Circle(2))\r\n    val jsonCircles = Json(circles)\r\n    val jsonCirclesFromString = Json.parse(jsonCircles.toString)\r\n    val resurrectedCircles = jsonCirclesFromString.as[List[Circle]]\r\n    assert(resurrectedCircles == circles)\r\n\r\n    // Here's a list of different shapes\r\n    val shapes = List[Shape](\r\n      Circle(1),\r\n      Square(2)\r\n    )\r\n\r\n    // Json(shapes) // won't work without serializer,\r\n    // Rapture Json nicely reports this:\r\n    // Please provide an implicit Serializer of type List[Shape]\r\n\r\n    // Since my use case is deserialization anyway,\r\n    // let's create the JSON shape list by hand\r\n    val mixed =\r\n      \"\"\"\r\n        |[\r\n        |{\r\n        |  \"type\" : \"Circle\",\r\n        |  \"shape\" : {\r\n        |     \"radius\" : 1\r\n        |   }\r\n        |},\r\n        |{\r\n        |  \"type\" : \"Square\",\r\n        |  \"shape\" : {\r\n        |     \"size\" : 2\r\n        |   }\r\n        |}\r\n        |]\r\n      \"\"\".stripMargin\r\n\r\n    // Here's a little helper class to find the type of the shape\r\n    // shape is typed to Any, this deserializes to play.api.libs.json.JsObject in my case\r\n    case class ShapeDefinition(`type`:String,shape:Any)\r\n\r\n    val mixedAsList = Json.parse(mixed).as[List[ShapeDefinition]]\r\n\r\n    val resurrectedShapes:List[Shape] = mixedAsList.map((item) => {\r\n\r\n      val stype = item.`type`\r\n      val shape = item.shape\r\n\r\n      // Here the shape is first converted to a string\r\n      // followed by Repture Json's case class deserialization power\r\n      stype match {\r\n        case \"Circle\" => Json.parse(shape.toString).as[Circle]\r\n        case \"Square\" => Json.parse(shape.toString).as[Square]\r\n      }\r\n    })\r\n\r\n    assert(resurrectedShapes == shapes)\r\n  }\r\n}\r\n```"},"comment":{"url":"https://api.github.com/repos/propensive/rapture-json/issues/comments/84654924","html_url":"https://github.com/propensive/rapture-json/issues/44#issuecomment-84654924","issue_url":"https://api.github.com/repos/propensive/rapture-json/issues/44","id":84654924,"user":{"login":"propensive","id":1024588,"avatar_url":"https://avatars.githubusercontent.com/u/1024588?v=3","gravatar_id":"","url":"https://api.github.com/users/propensive","html_url":"https://github.com/propensive","followers_url":"https://api.github.com/users/propensive/followers","following_url":"https://api.github.com/users/propensive/following{/other_user}","gists_url":"https://api.github.com/users/propensive/gists{/gist_id}","starred_url":"https://api.github.com/users/propensive/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/propensive/subscriptions","organizations_url":"https://api.github.com/users/propensive/orgs","repos_url":"https://api.github.com/users/propensive/repos","events_url":"https://api.github.com/users/propensive/events{/privacy}","received_events_url":"https://api.github.com/users/propensive/received_events","type":"User","site_admin":false},"created_at":"2015-03-22T17:04:27Z","updated_at":"2015-03-22T17:04:27Z","body":"Hi Manfred,\r\n\r\nFirstly, sorry for the delayed response -- I've been busy at Scala Days over the last week.\r\n\r\nI think it should be easier than that. You will need to define extractors and serializers for `Shape`. In the case of the extractor, this is a one-liner:\r\n\r\n```\r\nimplicit val ext: Extractor[Shape, Json] = Json.extractor[Circle] orElse Json.extractor[Square]\r\n```\r\n\r\nThis is basically saying \"compose an extractor which tries to extract a circle, and if that fails, tries to extract a square\".\r\n\r\nI would like it to be just as easy to define the serializer, but it's not that easy. You'll need to write this:\r\n\r\n```\r\nimplicit val ser: Serializer[Shape, Json] = Json.serializer[Json].contramap[Shape] {\r\n  case c: Circle => Json(c)\r\n  case s: Squore => Json(s)\r\n}\r\n```\r\n\r\nThis is saying \"take the serializer for `Json` (i.e. the no-op serializer), and map from a `Shape` type, handling each of the different cases\".\r\n\r\nDoes that do what you need it to?"}}