Использование Argonaut для создания универсального конвертера JSON

Я новичок в Scala, и здесь я пытаюсь создать универсальный конвертер json на основе Argonaut. Я пытался искать в 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), которые я добавлю в будущем.


person Wins    schedule 03.01.2014    source источник


Ответы (2)


Argonaut уже имеет общие функции кодирования и декодирования.

Например, Parse.decodeOption будет анализировать строку на любой тип, для которого у вас есть кодек.

То, что вы пытаетесь сделать, это решить во время выполнения, есть ли у вас кодек для типа, но вы можете заставить компилятор понять это за вас.

Возможность декодирования в тип 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].

person Ben James    schedule 03.01.2014
comment
Спасибо за ваш вклад, однако я забыл упомянуть, что на самом деле я пытаюсь создать реализацию Spring AbstractHttpMessageConverter, и поэтому я не могу изменить сигнатуру чтения или записи. Я обновил код, чтобы лучше отразить мой вопрос - person Wins; 04.01.2014

вам не нужен один компонент для обработки всех возможных классов, вы можете создать компонент для каждого класса, например.

class ArgonautConverter[T: CodecJson : ClassTag] extends AbstractHttpMessageConverter[T]    {    
  def supports(clazz) = clazz == classTag[T].runtimeClass
}
person OlegYch    schedule 04.01.2014
comment
Я не понимаю, что вы имеете в виду. Не могли бы вы подробнее рассказать о своем решении? Однако, если мне нужно создать конвертер для каждого отдельного класса, который я хочу преобразовать, это потребует больших усилий. Я не верю, что Scala не может делать то, что Java может делать очень легко. - person Wins; 04.01.2014