2016-05-17 49 views
1

在图书馆json4s调用构造函数,我打算写一些畸形数据弱类型的解串器(XML的主要结果 - > JSON转换)斯卡拉/ Java的反射编程:通过类型强制转换参数

我想动态程序获取给定构造函数的类型信息(简单,例如'Int'),将其应用于分析字符串(例如“12.51”),自动将字符串转换为类型(在这种情况下12.51应该被转换为13)然后调用构造函数。

我想出以下实现:

import org.json4s.JsonAST.{JDecimal, JDouble, JInt, JString} 
import org.json4s._ 
import scala.reflect.ClassTag 

object WeakNumDeserializer extends Serializer[Any] { 

    def cast[T](cc: Class[T], v: Any): Option[T] = { 
    implicit val ctg: ClassTag[T] = ClassTag(cc) 

    try { 
     Some(v.asInstanceOf[T]) 
    } 
    catch { 
     case e: Throwable => 
     None 
    } 
    } 

    override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Any] = Function.unlift{ 
    tuple: (TypeInfo, JValue) => 

     tuple match { 
     case (TypeInfo(cc, _), JInt(v)) => 
      cast(cc, v) 
     case (TypeInfo(cc, _), JDouble(v)) => 
      cast(cc, v) 
     case (TypeInfo(cc, _), JDecimal(v)) => 
      cast(cc, v) 
     case (TypeInfo(cc, _), JString(v)) => 
      cast(cc, v.toDouble) 
     case _ => 
      None 
     } 
    } 
} 

然而执行上的实际双=>内部情况下,上面的代码总是产生IllegalArgumentException异常。调试表明,该行:

v.asInstanceOf[T] 

不转换Double类型在内存中INT,仍是类型擦除后双号,它是在反射用于调用构造函数后,触发错误。

我如何绕过这一点,并使反射功能搞清楚这一点? 有没有办法告诉Java编译器实际将其转换为Int类型?

UPDATE:帮助验证你的答案我已经张贴了我的测试案例:

case class StrStr(
        a: String, 
        b: String 
       ) 

case class StrInt(
        a: String, 
        b: Int 
       ) 

case class StrDbl(
        a: String, 
        b: Double 
       ) 

case class StrIntArray(
         a: String, 
         b: Array[Int] 
        ) 

case class StrIntSeq(
         a: String, 
         b: Seq[Int] 
        ) 

case class StrIntSet(
         a: String, 
         b: Set[Int] 
        ) 

class WeakSerializerSuite extends FunSuite with TestMixin{ 

    implicit val formats = DefaultFormats ++ Seq(StringToNumberDeserializer, ElementToArrayDeserializer) 

    import org.json4s.Extraction._ 

    test("int to String") { 

    val d1 = StrInt("a", 12) 
    val json = decompose(d1) 

    val d2 = extract[StrStr](json) 
    d2.toString.shouldBe("StrStr(a,12)") 
    } 

    test("string to int") { 
    val d1 = StrStr("a", "12") 
    val json = decompose(d1) 

    val d2 = extract[StrInt](json) 
    d2.toString.shouldBe("StrInt(a,12)") 
    } 

    test("double to int") { 
    val d1 = StrDbl("a", 12.51) 
    val json = decompose(d1) 

    val d2 = extract[StrInt](json) 
    d2.toString.shouldBe("StrInt(a,12)") 
    } 

    test("int to int array") { 
    val d1 = StrInt("a", 12) 
    val json = decompose(d1) 

    val d2 = extract[StrIntArray](json) 
    d2.copy(b = null).toString.shouldBe("StrIntArray(a,null)") 
    } 

    test("int to int seq") { 
    val d1 = StrInt("a", 12) 
    val json = decompose(d1) 

    val d2 = extract[StrIntSeq](json) 
    d2.toString.shouldBe("StrIntSeq(a,List(12))") 
    } 

    test("int to int set") { 
    val d1 = StrInt("a", 12) 
    val json = decompose(d1) 

    val d2 = extract[StrIntSet](json) 
    d2.toString.shouldBe("StrIntSet(a,Set(12))") 
    } 

    test("string to int array") { 
    val d1 = StrStr("a", "12") 
    val json = decompose(d1) 

    val d2 = extract[StrIntArray](json) 
    d2.copy(b = null).toString.shouldBe("StrIntArray(a,null)") 
    } 

    test("string to int seq") { 
    val d1 = StrStr("a", "12") 
    val json = decompose(d1) 

    val d2 = extract[StrIntSeq](json) 
    d2.toString.shouldBe("StrIntSeq(a,List(12))") 
    } 

    test("string to int set") { 
    val d1 = StrStr("a", "12") 
    val json = decompose(d1) 

    val d2 = extract[StrIntSet](json) 
    d2.toString.shouldBe("StrIntSet(a,Set(12))") 
    } 

回答

0

我已经找到了第一个解决方案,TL:DR:它完全荒谬不合逻​​辑&,绝对充满boilerplates为一种建立的强类型语言。请发布您的答案认为更好:

abstract class WeakDeserializer[T: Manifest] extends Serializer[T] { 

// final val tpe = implicitly[Manifest[T]] 
// final val clazz = tpe.runtimeClass 

    // cannot serialize 
    override def serialize(implicit format: Formats): PartialFunction[Any, JValue] = PartialFunction.empty 
} 

object StringToNumberDeserializer extends WeakDeserializer[Any] { 

    override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Any] = { 

    case (TypeInfo(cc, _), JString(v)) => 
     cc match { 
     case java.lang.Byte.TYPE => v.toByte 
     case java.lang.Short.TYPE => v.toShort 
     case java.lang.Character.TYPE => v.toInt.toChar 
     case java.lang.Integer.TYPE => v.toInt 
     case java.lang.Long.TYPE => v.toLong 
     case java.lang.Float.TYPE => v.toFloat 
     case java.lang.Double.TYPE => v.toDouble 
     case java.lang.Boolean.TYPE => v.toBoolean 
     //TODO: add boxed type 
     } 
    } 
} 

object ElementToArrayDeserializer extends WeakDeserializer[Any] { 

    val listClass = classOf[List[_]] 
    val seqClass = classOf[Seq[_]] 
    val setClass = classOf[Set[_]] 
    val arrayListClass = classOf[java.util.ArrayList[_]] 

    override def deserialize(implicit format: Formats): PartialFunction[(TypeInfo, JValue), Any] = { 

    case ([email protected] TypeInfo(this.listClass, _), jv) => 
     List(extractInner(ti, jv, format)) 

    case ([email protected] TypeInfo(this.seqClass, _), jv) => 
     Seq(extractInner(ti, jv, format)) 

    case ([email protected] TypeInfo(this.setClass, _), jv) => 
     Set(extractInner(ti, jv, format)) 

    case ([email protected] TypeInfo(this.arrayListClass, _), jv) => 
     import scala.collection.JavaConverters._ 

     new java.util.ArrayList[Any](List(extractInner(ti, jv, format)).asJava) 

    case ([email protected] TypeInfo(cc, _), jv) if cc.isArray => 
     val a = Array(extractInner(ti, jv, format)) 
     mkTypedArray(a, firstTypeArg(ti)) 
    } 

    def mkTypedArray(a: Array[_], typeArg: ScalaType) = { 
    import java.lang.reflect.Array.{newInstance => newArray} 

    a.foldLeft((newArray(typeArg.erasure, a.length), 0)) { (tuple, e) => { 
     java.lang.reflect.Array.set(tuple._1, tuple._2, e) 
     (tuple._1, tuple._2 + 1) 
    }}._1 
    } 

    def extractInner(ti: TypeInfo, jv: JValue, format: Formats): Any = { 
    val result = extract(jv, firstTypeArg(ti))(format) 
    result 
    } 

    def firstTypeArg(ti: TypeInfo): ScalaType = { 
    val tpe = ScalaType.apply(ti) 
    val firstTypeArg = tpe.typeArgs.head 
    firstTypeArg 
    } 
}