spray-json库使用toJson
方法扩展了基本的Scala类型。如果基础类型有这样的pimp,我想将Any
转换为JsValue
。我最好的尝试有效,但是很冗长:
import cc.spray._
val maybeJson1: PartialFunction[Any, JsValue] = {
case x: BigDecimal => x.toJson
case x: BigInt => x.toJson
case x: Boolean => x.toJson
case x: Byte => x.toJson
case x: Char => x.toJson
case x: Double => x.toJson
case x: Float => x.toJson
case x: Int => x.toJson
case x: Long => x.toJson
case x: Short => x.toJson
case x: String => x.toJson
case x: Symbol => x.toJson
case x: Unit => x.toJson
}
理想情况下,我更喜欢这样的事情(不可能):
def maybeJson2(any: Any): Option[JsValue] = {
if (pimpExistsFor(any))
Some(any.toJson)
else
None
}
有没有办法在不枚举已经丰富的每种类型的情况下执行此操作?
答案 0 :(得分:3)
有一种方法,但它需要很多的反射,因此非常头疼。基本思路如下。 DefaultJsonProtocol
对象继承了一系列包含隐式对象的特征,这些对象包含write
个方法。每个都有一个访问器功能,但你不会知道它的名称。基本上,您只需使用所有不带参数的方法,并返回一个具有write
方法的对象,该方法接受对象的类并返回JsValue
。如果你找到一个这样的方法返回一个这样的类,使用反射来调用它。否则,保释。
它会像一样(警告,未经测试):
def canWriteMe(writer: java.lang.Class[_], me: java.lang.Class[_]):
Option[java.lang.reflect.Method] =
{
writer.getMethods.find(_.getName == "write").filter{ m =>
classOf[JsValue].isAssignableFrom(m.getReturnType) && {
val parm = m.getParameterTypes()
m.length == 1 && parm(0).isAssignableFrom(me)
}
}
}
def maybeJson2(any: Any): Option[JsValue] = {
val couldWork = {
DefaultJsonProtocol.getClass.getMethods.
filter(_.getParameterTypes.length==0).
flatMap(m => canWriteMe(m.getReturnType, any.getClass).map(_ -> m))
}
if (couldWork.length != 1) None else {
couldWork.headOption.map{ case (wrMeth, obMeth) =>
val wrObj = obMeth.invoke(DefaultJsonProtocol)
val answer = wrMeth.invoke(wrObj, any)
}
}
}
无论如何,你最好在REPL中逐步拉出DefaultJsonProtocol
类,找出如何可靠地识别定义编写器的对象,然后取出write
方法他们。
答案 1 :(得分:0)
我不确定它是否符合您的需求,但这是一种非常简单且类型安全的替代方法。
如果你保留了参数的类型(而不是使用Any
),你可以依靠隐式参数解析来在编译时找到正确的转换:
def toJson[T:JsonFormat]( t: T ): JsValue = implicitly[JsonFormat[T]].write(t)
您不需要选项,因为如果您尝试传递不是“pimpable”的参数,程序将在编译时失败。