I don’t know circe, but I’d be somewhat surprised if this was really required.
The most common mode for all these JSON libs is automagic mapping to Scala types through implicits/macros. At some lower level, they provide an API for manipulating their JSON object/array/value representations directly. Sometimes it may be convenient to mix, i.e. apply some custom low-level operations on the outer structure and defer handling of nested objects to the auto mapping level. (This seems to be going on in the blog post you refer to.) But you shouldn’t have to.
Here’s how I would approach your example using spray-json with json-lenses, sans error handling and the coordinate transmogrification you seem to apply.
def parsePerimeters(jstr: String): Map[String, List[List[Location]]] = {
def parseLocation(coords: List[Double]): Location =
coords match {
case List(lat, lon) => Location(lat, lon)
}
def parseFeature(jso: JsObject): (String, List[List[Location]]) = {
val name = jso.extract[String]("properties" / "name")
val geometry = jso.extract[JsObject]("geometry")
val coords =
geometry.extract[String]("type") match {
case "Polygon" => geometry.extract[List[List[List[Double]]]]("coordinates")
case "MultiPolygon" => geometry.extract[List[List[List[List[Double]]]]]("coordinates").map(_.flatten)
}
name -> coords.map(_.map(parseLocation))
}
jstr.parseJson.extract[JsObject]("features" / *).map(parseFeature).toMap
}
Result on your sample data:
Map(
Afghanistan -> List(List(Location(61.210817,35.650072), Location(62.230651,35.270664), Location(60.803193,34.404102), Location(61.210817,35.650072))),
Angola -> List(List(Location(16.326528,-5.87747), Location(16.57318,-6.622645)), List(Location(12.436688,-5.684304), Location(12.182337,-5.789931), Location(11.914963,-5.037987), Location(12.436688,-5.684304))),
Albania -> List(List(Location(20.590247,41.855404), Location(20.463175,41.515089), Location(20.605182,41.086226), Location(20.590247,41.855404))),
United Arab Emirates -> List(List(Location(51.579519,24.245497), Location(51.757441,24.294073), Location(51.579519,24.245497)))
)
I’d expect something similar to be possible with the circe low-level API, without having to introduce an intermediate type. (The type might still be a good idea for other reasons, but then it wouldn’t be “throwaway”.)