I'm new to Scala, and here I'm trying to create a generic json converter based on Argonaut. I've tried to search on google and stackoverflow, but so far I have no clue.
Here is the snippet of my code.
import org.springframework.http.converter.AbstractHttpMessageConverter
import org.springframework.http.{MediaType, HttpInputMessage, HttpOutputMessage}
import scala.io.Source
import argonaut._,Argonaut._
case class Currency(code: String)
object Currency {
implicit def CurrencyCodecJson: CodecJson[Currency] = casecodec1(Currency.apply, Currency.unapply)("code")
}
case class Person(firstName: String, lastName: String)
object Person {
implicit def PersonCodecJson: CodecJson[Person] = casecodec2(Person.apply, Person.unapply)("firstName", "LastName")
}
class ArgonautConverter extends AbstractHttpMessageConverter[Object](new MediaType("application", "json", Charset.forName("UTF-8")), new MediaType("application", "*+json", Charset.forName("UTF-8"))) {
val c = classOf[Currency]
val p = classOf[Person]
def writeInternal(t: Object, outputStream: OutputStream) = {
val jsonString = t match {
case c:Currency => c.asJson.ToString()
case p:Person => p.asJson.ToString()
}
def supports(clazz: Class[_]): Boolean = clazz.isAssignableFrom(classOf[CodecJson])// clazz == classOf[Currency] || clazz == classOf[LegalEntity]
def readInternal(clazz: Class[_ <: Object], inputStream: InputStream): Object = {
val jsonString = Source.fromInputStream(inputStream).getLines.mkString
val jsonObject = clazz match {
case `c` => jsonString.decodeOption[Currency]
case `p` => jsonString.decodeOption[Person]
}
jsonObject match {
case Some(j) => j
case None => null
}
}
}
What I'm trying to do is to generalize such that I don't need to keep adding the match for every new model class (like Currency and Person in this case) that I will add in the future.
Argonaut already has generic encoding and decoding functions.
For example,
Parse.decodeOptionwill parse a String into any type for which you have a codec.What you are trying to do is decide at runtime whether you have a codec for a type, but you can make the compiler figure that out for you.
Whether you can decode to a type
Tdepends on whether there is an implicit instance ofDecodeJson[T]in scope. (That is a supertype ofCodecJson[T], and you have written a couple of those, so they're good.)Unfortunately, the compiler won't infer this constraint for you, so you have to mention it in the type signature. One way to do this is to use a context bound, which is the
T : DecodeJsonin the example below.(Also, note that you should really return
Option[T]instead of usingnull.)Similarly,
writecould be implemented with the signature:Your
CodecJson[T]instances are also instances ofEncodeJson[T].