Reputation: 12439
I'm not a fan of bringing implicit parameters into my code so where I use them I want to encapsulate their use. So I am trying to define an object that both wraps up calls to spray-json with exception handling and contains default implicit JsonFormats for each of my model classes. However the implicit parameters are not resolved unless they are imported into the client, calling code, which is exactly where I don't want them to be. Here's what I have so far (which doesn't resolve the implicit formatters), is there a way I can get what I want to work?
package com.rsslldnphy.json
import com.rsslldnphy.models._
import spray.json._
object Json extends DefaultJsonProtocol {
implicit val personFormat = jsonFormat1(Person)
implicit val animalFormat = jsonFormat1(Animal)
def parse[T](s:String)(implicit reader: JsonReader[T]): Option[T] = {
try { Some(JsonParser(s).convertTo[T]) }
catch { case e: DeserializationException => None }
}
}
NB. a JsonFormat is a type of JsonReader
EDIT: Here's what I've written based on @paradigmatic's second suggestion (which I can't get to work, I still get Cannot find JsonReader or JsonFormat type class for T
). Am I missing something?
object Protocols extends DefaultJsonProtocol {
implicit val personFormat = jsonFormat1(Person)
implicit val animalFormat = jsonFormat1(Animal)
}
object Json {
def parse[T](s:String): Option[T] = {
import Protocols._
try { Some(JsonParser(s).convertTo[T]) }
catch { case e: DeserializationException => None }
}
}
For the record, this is a code snippet that does work, but that I'm trying to avoid as it requires too much of the client code (ie. it needs to have the implicits in scope):
object Json extends DefaultJsonProtocol {
implicit val personFormat = jsonFormat1(Person)
implicit val animalFormat = jsonFormat1(Animal)
}
object ClientCode {
import Json._
def person(s: String): Person = JsonParser(s).convertTo[Person]
}
Upvotes: 2
Views: 2522
Reputation: 40461
You could declare the implicits in the companion objects:
object Person {
implicit val personFormat: JReader[Person] = jsonFormat1(Person)
}
object Animal {
implicit val animalFormat: JReader[Animal] = jsonFormat1(Animal)
}
The implicit resolution rules are very complex. You can find more information in this blog post. But if the compiler is looking for a typeclass T[A]
, it will look (soon or later) for it in the companion object of class/trait A
.
EDIT: If the issue is only a problem of scope "pollution", you could just introduce some braces. With your code example, you could call the function parse as:
package com.rsslldnphy.json
import com.rsslldnphy.models._
import spray.json._
object Json extends DefaultJsonProtocol {
implicit val personFormat = jsonFormat1(Person)
implicit val animalFormat = jsonFormat1(Animal)
def parse[T](s:String)(implicit reader: JsonReader[T]): Option[T] = {
try { Some(JsonParser(s).convertTo[T]) }
catch { case e: DeserializationException => None }
}
}
object JsonFacade {
def optParse[T]( s: String ): Option[T] = {
import Json._
parse[T]( s )
}
}
Here the implicits "pollutes" only the optParse
method.
Upvotes: 7