4

我是 Scala 的新手,在这里我试图创建一个基于 Argonaut 的通用 json 转换器。我曾尝试在 google 和 stackoverflow 上进行搜索,但到目前为止我一无所知。

这是我的代码片段。

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
        }
    }
}

我要做的是进行概括,这样我就不需要为将来添加的每个新模型类(如本例中的 Currency 和 Person)继续添加匹配项。

4

2 回答 2

6

Argonaut 已经具有通用的编码和解码功能。

例如,Parse.decodeOption将 String 解析为您拥有编解码器的任何类型。

您要做的是在运行时决定您是否有一个类型的编解码器,但您可以让编译器为您解决这个问题。

是否可以解码为类型T取决于DecodeJson[T]范围内是否存在隐式实例。(这是 的超类型CodecJson[T],你已经写了几个,所以它们很好。)

不幸的是,编译器不会为您推断此约束,因此您必须在类型签名中提及它。一种方法是使用上下文绑定T : DecodeJson如下例所示。

def read[T : DecodeJson](inputStream: InputStream): Option[T] = {
  val jsonString = Source.fromInputStream(inputStream).getLines.mkString
  Parse.decodeOption(jsonString)
}

(另外,请注意,您应该真正返回Option[T]而不是使用null.)

同样,write可以用签名实现:

def write[T : EncodeJSON](t: T, outputStream: OutputStream)

您的CodecJson[T]实例也是EncodeJson[T].

于 2014-01-03T11:09:28.137 回答
2

您不需要单个 bean 来处理每个可能的类,您可以为每个类创建一个 bean,例如

class ArgonautConverter[T: CodecJson : ClassTag] extends AbstractHttpMessageConverter[T]    {    
  def supports(clazz) = clazz == classTag[T].runtimeClass
}
于 2014-01-04T13:26:36.203 回答