大小写 class 和内部大小写 classes 的字符串列表
List of strings to case class with inner case classes
假设我有 2 个案例 classes:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
有什么好方法可以将 "translate" 字符串列表 class 人类?像:
def superMethod[A](params: List[String]): A = ???
val params: List[Any] = List("john", 100, "dollar")
superMethod(params) // => Human("john", Money(100, "dollar"))
所以基本上我只在运行时知道类型 A
更新:我找到了~我一直在寻找的东西。看来我可以通过 shapeless 做到这一点。 example 我在 github
中找到
如果 A 不是泛型类型,而是有效的 Human,您可以使用伴随对象来处理 class Human:
object Human {
def fromList(list: List[String]): Human = list match {
case List(name, amount, currency) => Human(name, Money(amount.toInt, currency))
case _ => handle corner case
}
}
你可以打电话给:
Human.fromList(List("john", "100", "dollar"))
为了安全起见,不要忘记处理大小不为 3 的列表;以及其第二个元素无法转换为 Int 的列表:
import scala.util.Try
object Human {
def fromList(list: List[String]): Option[Human] = list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case _ => None
}
}
编辑:根据您最后的评论,您可能会发现这很有用:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
case class SomethingElse(whatever: Double)
object Mapper {
def superMethod(list: List[String]): Option[Any] =
list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case List(whatever) => Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}
}
println(Mapper.superMethod(List("john", 100, "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod(List(17d)))
> Some(SomethingElse(17.0))
或者:
object Mapper {
def superMethod[A](list: List[String]): Option[A] =
(list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount, currency))).toOption
case List(whatever) =>
Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}).map(_.asInstanceOf[A])
}
println(Mapper.superMethod[Human](List("john", "100", "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod[SomethingElse](List("17.2")))
> Some(SomethingElse(17.0))
这是一个适用于通用 classes A
.
的实现
它依赖于运行时反射(即可以在运行时向方法传递不同的TypeTag
)。要使用此方法,必须满足以下明显条件:
A
必须在 class 路径上,否则可以由使用的 class 加载程序加载
TypeTag
必须在呼叫站点可供 A
使用。
实际实现在Deserializer
对象中。然后是一个小演示。
解串器:
import scala.reflect.runtime.universe.{TypeTag, Type}
object Deserializer {
/** Extracts an instance of type `A` from the
* flattened `Any` constructor arguments, and returns
* the constructed instance together with the remaining
* unused arguments.
*/
private def deserializeRecHelper(
flattened: List[Any],
tpe: Type
): (Any, List[Any]) = {
import scala.reflect.runtime.{universe => ru}
// println("Trying to deserialize " + tpe + " from " + flattened)
// println("Constructor alternatives: ")
// val constructorAlternatives = tpe.
// member(ru.termNames.CONSTRUCTOR).
// asTerm.
// alternatives.foreach(println)
val consSymb = tpe.
member(ru.termNames.CONSTRUCTOR).
asTerm.
alternatives(0).
asMethod
val argsTypes: List[Type] = consSymb.paramLists(0).map(_.typeSignature)
if (tpe =:= ru.typeOf[String] || argsTypes.isEmpty) {
val h :: t = flattened
(h, t)
} else {
val args_rems: List[(Any, List[Any])] = argsTypes.scanLeft(
(("throwaway-sentinel-in-deserializeRecHelper": Any), flattened)
) {
case ((_, remFs), t) =>
deserializeRecHelper(remFs, t)
}.tail
val remaining: List[Any] = args_rems.last._2
val args: List[Any] = args_rems.unzip._1
val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
val classMirror = runtimeMirror.reflectClass(tpe.typeSymbol.asClass)
val cons = classMirror.reflectConstructor(consSymb)
// println("Build constructor arguments array for " + tpe + " : " + args)
val obj = cons.apply(args:_*)
(obj, remaining)
}
}
def deserialize[A: TypeTag](flattened: List[Any]): A = {
val (a, rem) = deserializeRecHelper(
flattened,
(implicitly: TypeTag[A]).tpe
)
require(
rem.isEmpty,
"Superfluous arguments remained after deserialization: " + rem
)
a.asInstanceOf[A]
}
}
演示:
case class Person(id: String, money: Money, pet: Pet, lifeMotto: String)
case class Money(num: Int, currency: String)
case class Pet(color: String, species: Species)
case class Species(description: String, name: String)
object Example {
def main(args: Array[String]): Unit = {
val data = List("Bob", 42, "USD", "pink", "invisible", "unicorn", "what's going on ey?")
val p = Deserializer.deserialize[Person](data)
println(p)
}
}
输出:
Person(Bob,Money(42,USD),Pet(pink,Species(invisible,unicorn)),what's going on ey?)
讨论
此实现不限于 classes 的情况,但它要求每个“Tree-node-like”class 都有一个接受任一
的构造函数
- 基本类型(Int、Float),或
- 字符串,或
- 其他“Tree-node-like”classes.
请注意,该任务有点 ill-posed:说所有构造函数参数都被压平在一个列表中是什么意思?给定 class Person(name: String, age: Int)
,List[Any]
是否会包含 name
的每个字节作为单独的条目?可能不会。因此,字符串由反序列化器以特殊方式处理,并且出于相同原因不支持所有其他 collection-like 实体(不清楚在哪里停止解析,因为集合的大小未知)。
假设我有 2 个案例 classes:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
有什么好方法可以将 "translate" 字符串列表 class 人类?像:
def superMethod[A](params: List[String]): A = ???
val params: List[Any] = List("john", 100, "dollar")
superMethod(params) // => Human("john", Money(100, "dollar"))
所以基本上我只在运行时知道类型 A
更新:我找到了~我一直在寻找的东西。看来我可以通过 shapeless 做到这一点。 example 我在 github
中找到如果 A 不是泛型类型,而是有效的 Human,您可以使用伴随对象来处理 class Human:
object Human {
def fromList(list: List[String]): Human = list match {
case List(name, amount, currency) => Human(name, Money(amount.toInt, currency))
case _ => handle corner case
}
}
你可以打电话给:
Human.fromList(List("john", "100", "dollar"))
为了安全起见,不要忘记处理大小不为 3 的列表;以及其第二个元素无法转换为 Int 的列表:
import scala.util.Try
object Human {
def fromList(list: List[String]): Option[Human] = list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case _ => None
}
}
编辑:根据您最后的评论,您可能会发现这很有用:
case class Money(amount: Int, currency: String)
case class Human(name: String, money: Money)
case class SomethingElse(whatever: Double)
object Mapper {
def superMethod(list: List[String]): Option[Any] =
list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount.toInt, currency))).toOption
case List(whatever) => Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}
}
println(Mapper.superMethod(List("john", 100, "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod(List(17d)))
> Some(SomethingElse(17.0))
或者:
object Mapper {
def superMethod[A](list: List[String]): Option[A] =
(list match {
case List(name, amount, currency) =>
Try(Human(name, Money(amount, currency))).toOption
case List(whatever) =>
Try(SomethingElse(whatever.toDouble)).toOption
case _ => None
}).map(_.asInstanceOf[A])
}
println(Mapper.superMethod[Human](List("john", "100", "dollar")))
> Some(Human(john,Money(100,dollar)))
println(Mapper.superMethod[SomethingElse](List("17.2")))
> Some(SomethingElse(17.0))
这是一个适用于通用 classes A
.
它依赖于运行时反射(即可以在运行时向方法传递不同的TypeTag
)。要使用此方法,必须满足以下明显条件:
A
必须在 class 路径上,否则可以由使用的 class 加载程序加载TypeTag
必须在呼叫站点可供A
使用。
实际实现在Deserializer
对象中。然后是一个小演示。
解串器:
import scala.reflect.runtime.universe.{TypeTag, Type}
object Deserializer {
/** Extracts an instance of type `A` from the
* flattened `Any` constructor arguments, and returns
* the constructed instance together with the remaining
* unused arguments.
*/
private def deserializeRecHelper(
flattened: List[Any],
tpe: Type
): (Any, List[Any]) = {
import scala.reflect.runtime.{universe => ru}
// println("Trying to deserialize " + tpe + " from " + flattened)
// println("Constructor alternatives: ")
// val constructorAlternatives = tpe.
// member(ru.termNames.CONSTRUCTOR).
// asTerm.
// alternatives.foreach(println)
val consSymb = tpe.
member(ru.termNames.CONSTRUCTOR).
asTerm.
alternatives(0).
asMethod
val argsTypes: List[Type] = consSymb.paramLists(0).map(_.typeSignature)
if (tpe =:= ru.typeOf[String] || argsTypes.isEmpty) {
val h :: t = flattened
(h, t)
} else {
val args_rems: List[(Any, List[Any])] = argsTypes.scanLeft(
(("throwaway-sentinel-in-deserializeRecHelper": Any), flattened)
) {
case ((_, remFs), t) =>
deserializeRecHelper(remFs, t)
}.tail
val remaining: List[Any] = args_rems.last._2
val args: List[Any] = args_rems.unzip._1
val runtimeMirror = ru.runtimeMirror(getClass.getClassLoader)
val classMirror = runtimeMirror.reflectClass(tpe.typeSymbol.asClass)
val cons = classMirror.reflectConstructor(consSymb)
// println("Build constructor arguments array for " + tpe + " : " + args)
val obj = cons.apply(args:_*)
(obj, remaining)
}
}
def deserialize[A: TypeTag](flattened: List[Any]): A = {
val (a, rem) = deserializeRecHelper(
flattened,
(implicitly: TypeTag[A]).tpe
)
require(
rem.isEmpty,
"Superfluous arguments remained after deserialization: " + rem
)
a.asInstanceOf[A]
}
}
演示:
case class Person(id: String, money: Money, pet: Pet, lifeMotto: String)
case class Money(num: Int, currency: String)
case class Pet(color: String, species: Species)
case class Species(description: String, name: String)
object Example {
def main(args: Array[String]): Unit = {
val data = List("Bob", 42, "USD", "pink", "invisible", "unicorn", "what's going on ey?")
val p = Deserializer.deserialize[Person](data)
println(p)
}
}
输出:
Person(Bob,Money(42,USD),Pet(pink,Species(invisible,unicorn)),what's going on ey?)
讨论
此实现不限于 classes 的情况,但它要求每个“Tree-node-like”class 都有一个接受任一
的构造函数- 基本类型(Int、Float),或
- 字符串,或
- 其他“Tree-node-like”classes.
请注意,该任务有点 ill-posed:说所有构造函数参数都被压平在一个列表中是什么意思?给定 class Person(name: String, age: Int)
,List[Any]
是否会包含 name
的每个字节作为单独的条目?可能不会。因此,字符串由反序列化器以特殊方式处理,并且出于相同原因不支持所有其他 collection-like 实体(不清楚在哪里停止解析,因为集合的大小未知)。