lundi 16 mai 2016

Scala/Java reflective programming: invoke constructor by typecasted arguments

In the library json4s, I intend to write a weakly typed deserializer for some malformed data (mostly the result of XML -> JSON conversions)

I want the dynamic program to get the type information of a given constructor (easy, e.g. 'Int'), apply it on a parsed string (e.g. "12.51"), automatically convert string into the type (in this case 12.51 should be typecasted to 13), then call the constructor.

I come up with the following implementation:

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

However executing the above code on a real Double => Int case always yield IllegalArgumentException. Debugging reveals that the line:

v.asInstanceOf[T]

does not convert Double type to Int in memory, it remains as a Double number after type erasure, and after it is used in reflection to call the constructor it triggers the error.

How do I bypass this and make the reflective function figuring this out? Is there a way to tell the Java compiler to actually convert it into an Int type?





Aucun commentaire:

Enregistrer un commentaire