如何基于浓缩动态调度?

时间:2012-05-26 22:47:08

标签: json scala spray enrich-my-library

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  
}

有没有办法在不枚举已经丰富的每种类型的情况下执行此操作?

2 个答案:

答案 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”的参数,程序将在编译时失败。