In Scala 2.10.6, using json4s 3.2.11, to which I'm constrained for now, using the following code:
object Thing1 extends Enumeration {
type Thing1 = Value
val A = Value(0, "A")
val B = Value(1, "B")
}
object Thing2 extends Enumeration {
type Thing2 = Value
val A = Value(0, "A")
val B = Value(1, "B")
val C = Value(2, "C")
}
case class ThingHolder(thing1: Thing1, thing2: Thing2)
class ThingSpec extends FlatSpec with Matchers {
"desrialized" should "match original" in {
import org.json4s.native.Serialization.writePretty
implicit val formats = DefaultFormats + new EnumNameSerializer(Thing1) + new EnumNameSerializer(Thing2)
val original = ThingHolder(Thing1.A, Thing2.C)
println(original)
val serialized = writePretty(original)
println(serialized)
val jValue = JsonParser.parse(serialized)
val deserialized = jValue.camelizeKeys.extract[ThingHolder]
println(deserialized)
println(deserialized.thing1)
deserialized should be(original)
}
}
Results in:
ThingHolder(A,C)
{
"thing1":"A",
"thing2":"C"
}
ThingHolder(A,C)
A
ThingHolder(A,C) was not equal to ThingHolder(A,C)
This occurs with Enumeration, but not another type, for example, String. If the case class has only one Enumeration, it works fine. Why is that? I can correct this by deserializing to a case class with Strings, then mapping to the case class as I want it to be. Is there a way to coax the json4s to deserialize directly such that the deserialized object matches the original?
EDIT:
Here's the hack I mentioned to do the mapping:
case class ThingHolderSerialized(thing1: String, thing2: String)
...
val deserialized = jValue.camelizeKeys.extract[ThingHolderSerialized]
val reconstituted = ThingHolder(Thing1.withName(deserialized.thing1), Thing2.withName(deserialized.thing2))
EDIT:
Actually, the separate serializer class is unnecessary:
val deserialized = jValue.camelizeKeys.extract[ThingHolder]
val reconstituted = ThingHolder(Thing1.withName(deserialized.thing1.toString), Thing2.withName(deserialized.thing2.toString))
EDIT:
It appears that it's thing1 that's not properly deserialized, since this also builds a matching reconstituted object:
val reconstituted = deserialized.copy(thing1 = Thing1.withName(deserialized.thing1.toString))
but not this:
val reconstituted = deserialized.copy(thing2 = Thing2.withName(deserialized.thing2.toString))
I'm putting this out there as a solution for people that need to support json4s case class deserialization in the presence of multiple Enumerations. It's not the answer I'm looking for, but works in the meantime:
object Thing1 extends Enumeration {
type Thing1 = Value
val A = Value(0, "A")
val B = Value(1, "B")
}
object Thing2 extends Enumeration {
type Thing2 = Value
val A = Value(0, "A")
val B = Value(1, "B")
val C = Value(2, "C")
}
case class ThingHolder(thing1: Thing1, thing2: Thing2)
class ThingSpec extends FlatSpec with Matchers {
"reconstituted" should "match original" in {
import org.json4s.native.Serialization.writePretty
implicit val formats = DefaultFormats + new EnumNameSerializer(Thing1) + new EnumNameSerializer(Thing2)
val original = ThingHolder(Thing1.A, Thing2.C)
println(original)
val serialized = writePretty(original)
println(serialized)
val jValue = JsonParser.parse(serialized)
val deserialized = jValue.camelizeKeys.extract[ThingHolder]
println(deserialized)
val reconstituted = deserialized.copy(thing1 = Thing1.withName(deserialized.thing1.toString))
reconstituted should be(original)
}
}
The key is in the line where we're building reconstituted. We make a copy, and in the process, convert thing1 to a string and back to an Enumeration. Fortunately, toString works without the field being correctly deserialized.
Related
Suppose I have a MyItem trait and its companion object has an apply() function that creates a class instance called SubItem that extends from MyItem:
import scala.reflect.runtime.{universe => ru}
trait MyItem {
import MyItem._
def num: Option[Int]
}
object MyItem {
class SubItem(val num: Option[Int]) extends MyItem
def apply(num: Option[Int]): MyItem = new SubItem(num) // creates SubItem
}
def getTypeTag[T: ru.TypeTag](obj: T) = ru.typeTag[T]
val modifiedItem = MyItem(Some(11))
val theType = getTypeTag(modifiedItem).tpe
If you print out theType above, it will be MyItem.
At this point if you try to use reflection to modify the field num, it's not going to work because MyItem has num as a method, not a field (as in MyItem.SubItem):
val m = ru.runtimeMirror(modifiedItem.getClass.getClassLoader)
val numTermSymb = theType.decl(ru.TermName("num")).asTerm
val im = m.reflect(modifiedItem)
val numFieldMirror = im.reflectField(numTermSymb) // not going to work
numFieldMirror.get
numFieldMirror.set(Some(999)) // my goal, if possible
Unfortunately, above will throw out scala.ScalaReflectionException: expected a field or an accessor method symbol, you provided method num.
Instead you should do the following:
val numTermSymb = theType.decl(ru.TermName("num")).asMethod
val im = m.reflect(modifiedItem)
val numFieldMirror = im.reflectMethod(numTermSymb)
numFieldMirror() // returns `Some(11)`
But my goal is to access the SubItem class that extends MyItem and modify its field. How can I get an instance of the type MyItem and modify the field in MyItem.SubItem that MyItem's method num is accessing?
Replace
val theType = getTypeTag(modifiedItem).tpe
with
val theType = ru.typeOf[MyItem.SubItem]
if you know the name of class statically or with
val theType = m.staticClass("pckg.MyItem.SubItem").typeSignature
if you know the name of class dynamically.
Try
val className = modifiedItem.getClass.getName.replace('$', '.')
val theType = m.staticClass(className).typeSignature
Actually, m.staticClass(className).typeSignature is AnyRef with pckg.MyItem {...} i.e. parents/decls of SubItem
theType =:= ru.typeOf[MyItem.SubItem] // false
so, although numFieldMirror.get/set work, it's better to use toType instead of typeSignature
val className = modifiedItem.getClass.getName.replace('$', '.')
val theType = m.staticClass(className).toType
theType =:= ru.typeOf[MyItem.SubItem] // true
One more way is purely Scala-like
val instanceMirror = m.reflect(modifiedItem)
val theType = instanceMirror.symbol.toType
theType =:= ru.typeOf[MyItem.SubItem] // true
It's even better because doesn't use error-prone and implementation-dependent operations on strings (replace).
I have the following piece of code
class GM
class BM extends GM
case class GenException(message: String) extends RuntimeException(message) {
override def toString: String = s"${this.toString()}"
}
case class Hello[T <: GM](t: Try[T])
object Main extends App {
val first: Hello[BM] = Hello.apply(Success.apply(new BM))
val second: Hello[Nothing] = first.copy(t = Failure(GenException("hello")))
}
As you can see, scala inferred the type of second as Hello[Nothing], but i'd want it to preserve the type the object had on it, i.e. it's a computation failure on type BM
Write
val second: Hello[BM] = first.copy(t = Failure(GenException("hello")))
Scala infers type Hello[Nothing] because you write Hello[Nothing] explicitly.
import scala.reflect.runtime.universe.TypeTag
def getTypeTag[T](v: T)(implicit typeTag: TypeTag[T]): TypeTag[T] = typeTag
val second = first.copy(t = Failure(GenException("hello")))
println(getTypeTag(second)) //TypeTag[Hello[Nothing]]
val second: Hello[Nothing] = first.copy(t = Failure(GenException("hello")))
println(getTypeTag(second))//TypeTag[Hello[Nothing]]
val second: Hello[BM] = first.copy(t = Failure(GenException("hello")))
println(getTypeTag(second))//TypeTag[Hello[BM]]
Is there any way to dynamically instantiate an Enumeration#Value in Scala?
So far I have:
object Letter extends Enumeration {
val A,B,C = Value
}
// fieldType is of type Universe.Type for the field in my case class, which happens to
// be of type Letter.Value
val ftype = fieldType.typeSymbol.name.toString
val enumVal = "B" // a valid Enumeration.Value
val erasedEnumType = fieldType.asInstanceOf[TypeRef] // Letter
Now what? I'm trying to arrive at an object of value Letter.B in this case.
I saw this clip on another posting:
def create[T <: Enum[T]](clazz: Class[T], input: String): T = Enum.valueOf(clazz, input)
I couldn't make this work because I don't have "T" at compile-time (I'm parsing this value from input strings at runtime).
You mean retrieve instead of instantiate?
scala> object Letter extends Enumeration {
| val A,B,C = Value
| }
defined module Letter
scala> Letter withName "B"
res0: Letter.Value = B
as opposed to creating another value.
Updated:
package reflectenum
import scala.reflect.runtime.universe._
import scala.reflect.runtime.{currentMirror=>cm}
import scala.reflect.NameTransformer._
object Letters extends Enumeration {
val A,B,C = Value
}
object Test extends App {
val claas = cm.classLoader loadClass "reflectenum.Letters$"
Console println s"$claas"
val enum = claas.getField(MODULE_INSTANCE_NAME).get(null).asInstanceOf[Enumeration]
Console println s"$enum"
Console println s"${enum withName "B"}"
// given some element of the enumeration
val v = enum withName "B"
val im = cm reflect v
val outerName = newTermName("scala$Enumeration$$outerEnum")
val outer = typeOf[enum.Value] member outerName
val f = im reflectField outer.asTerm.accessed.asTerm
assert(enum == f.get)
}
This works:
val foo = Class.forName("reflectnum.Letter$") //← note extra $
val anObj = foo.getField("MODULE$").get(foo)
val meth = anObj.getClass.getMethod("withName",classOf[String])
val z = meth.invoke(anObj,"B")
I've used the accepted answer of this question to build little helper class to construct case classes from arrays of values:
construct case class from collection of parameters
As mentioned in the answer, inner case classes don't work. You get a
ScalaReflectionException: class X is an inner class, use reflectClass on an InstanceMirror to obtain its ClassMirror
I'm having trouble figuring out how to use an InstanceMirror as the exception explains. Here's my current REPL-able code (slightly more complex than needed because of the cache).
import scala.reflect.runtime.universe._
object ReflectUtil {
private val constructorCache = collection.mutable.HashMap[String, MethodMirror]()
def constructFromSeq[T: TypeTag](args: Seq[Any]): T = {
val tag = typeTag[T]
val strTag = tag.toString
var constructorMirror = constructorCache.getOrElseUpdate( strTag, {
val rm = runtimeMirror(getClass.getClassLoader)
val classSymbol = tag.tpe.typeSymbol.asClass
val classMirror = rm.reflectClass(classSymbol) // wrap with try/catch?
val constructorSymbol = tag.tpe.declaration(nme.CONSTRUCTOR).asMethod
classMirror.reflectConstructor(constructorSymbol)
})
constructorMirror(args: _*).asInstanceOf[T]
}
}
case class X(a:String, b:Int)
class Out {
case class Y(a:String, b:Int)
def buildY(arr:Array[Any]) = {
ReflectUtil.constructFromSeq[Y](arr)
}
}
val arr = Array[Any]("asdf", 1234)
ReflectUtil.constructFromSeq[X](arr) // this works
val inst = new Out
inst.buildY(arr) // this doesn't work
What exception implies is that you need an outer reference to construct an instance of an inner class (e.g. to construct Y you need an instance of Out). Then you could do rm.reflect(instanceOfOut).reflectClass....
I have a simple enum like this:
object ConditionOperator extends Enumeration {
val Equal = Value("equal")
val NotEqual = Value("notEqual")
val GreaterOrEqual = Value("greaterOrEqual")
val Greater = Value("greater")
val LessOrEqual = Value("lessOrEqual")
val Less = Value("less")
And I'd like to add a method to each enum so that I can use it like this:
def buildSqlCondition(field: String, operator: ConditionOperator.Value, value: String ) = {
val sqlOperator = operator.toSql
[...]
So, ConditionOperator.Equal.toSql wuld return "=", and ConditionOperator.NotEqual.toSql would return "<>", etc...
But I don't know how to define a toSql method, so that each enum can "see" it's own value and decide how to translate itself to a sql operator...
This is an example of what I have found for Scala 2.9.2 from various searches on the topic in the past:
object Progress extends Enumeration {
type enum = Value
val READY = new ProgressVal {
val isActive = false
def myMethod: Any = { .. }
}
val EXECUTE = new ProgressVal {
val isActive = true
def myMethod: Any = { .. }
}
val COMPLETE = new ProgressVal {
val isActive = false
def myMethod: Any = { .. }
}
protected abstract class ProgressVal extends Val() {
val isActive: Boolean
def myMethod: Any
}
implicit def valueToProgress(valu: Value) = valu.asInstanceOf[ProgressVal]
}
type Progress = Progress.enum
The implicit is key to making this usable.
The type enum and type Progress are somewhat redundant; I include them to present both concepts as something I've found helpful.
To give credit where it's due, the original idea for this came from Sean Ross in a response to a question of which this one is a duplicate.
You can start by defining an inner class that overrides Enumeration.Val. To simplify things, let's call it Value (we overload the original meaning of Value as defined in Enumeration).
So we have our new Value type which inherits Enumeration.Val which itself inherits Enumeration.Value.
Given that toSql has no side effects and returns a different string for each enumeration, you might as well just make it a val.
Finally, to make it fully usable, you'll want to have ConditionOperator.apply and ConditionOperator.withName to return your newly defined Value class instead of the Value type as defined in Enumeration.
Otherwise, when using apply or withName to look up an instance of ConditionOperator by index/name, you won't be able to call toSql because the enumeration type will not be specific enoough.
Ideally we'd like to just override apply and withName and add a cast to ConditionOperator.Value, but these methods are final.
However we can employ a small trick here: define new methods apply and withName with the same signature but an additional implicit parameter that will always be available (Predef.DummyImplicit fits this rolle perfectly).
The additional parameter ensures that the signature is different so that we are able to define these new methods, while at the same time being nearly indistinguishable from the original apply/withName methods.
The rules for overloading resolution in scala ensure that our new methods are the ones favored by the compiler (so we have in practice shadowed the original methods).
object ConditionOperator extends Enumeration {
// Here we overload the meaning of "Value" to suit our needs
class Value(name: String, val toSql: String) extends super.Val(name) {
def someFlag: Boolean = true // An example of another method, that you can override below
}
val Equal = new Value("equal", "=")
val NotEqual = new Value("notEqual", "<>")
val GreaterOrEqual = new Value("greaterOrEqual", ">=")
val Greater = new Value("greater", ">")
val LessOrEqual = new Value("lessOrEqual", "<=") { override def someFlag = false }
val Less = new Value("less", "<")
final def apply(x: Int)( implicit dummy: DummyImplicit ): Value = super.apply(x).asInstanceOf[Value]
final def withName(s: String)( implicit dummy: DummyImplicit ): Value = super.withName(s).asInstanceOf[Value]
}
You can check that you can now do things like ConditionOperator(2).toSql or ConditionOperator.withName("greaterOrEqual"), which both return ">=" as expected.
Finally, the above gymnastic can be abstracted away:
abstract class CustomEnumeration extends Enumeration {
type BaseValue = super.Val
type CustomValue <: super.Value
type Value = CustomValue
final def apply(x: Int)( implicit dummy: DummyImplicit ): CustomValue = super.apply(x).asInstanceOf[CustomValue]
final def withName(s: String)( implicit dummy: DummyImplicit ): CustomValue = super.withName(s).asInstanceOf[CustomValue]
}
object ConditionOperator extends CustomEnumeration {
class CustomValue(name: String, val toSql: String) extends BaseValue(name) {
def someFlag: Boolean = true
}
val Equal = new Value("equal", "=")
val NotEqual = new Value("notEqual", "<>")
val GreaterOrEqual = new Value("greaterOrEqual", ">=")
val Greater = new Value("greater", ">")
val LessOrEqual = new Value("lessOrEqual", "<=") { override def someFlag = false }
val Less = new Value("less", "<")
}
object ConditionOperator extends Enumeration {
implicit def overrideValue(v:Value) = new OverridedValue(v)
class OverridedValue(val v:Value) {
def toSql = v.toString
}
val Equal = Value("=")
val NotEqual = Value("<>")
val GreaterOrEqual = Value(">=")
val Greater = Value(">")
val LessOrEqual = Value("<=")
val Less = Value("<")
}
import ConditionOperator._
assert(Equal.toSql == "=")
And in scala 2.10, you can make it simpler by using implicit class, replace
implicit def overrideValue(v:Value) = new OverridedValue(v)
class OverridedValue(val v:Value) {
def toSql = v.toString
}
with
implicit class OverridedValue(val v:Value) {
def toSql = v.toString
}