我想读一个字符串作为案例类的实例。例如,如果函数名为“read”,它将允许我执行以下操作:
case class Person(name: String, age: Int)
val personString: String = "Person(Bob,42)"
val person: Person = read(personString)
这与Haskell中的读取类型类相同。
答案 0 :(得分:12)
dflemstr在设置实际read
方法方面做出了更多回答 - 我将为实际的解析方法做出更多回答。
我的方法有两个可以在scala的模式匹配块中使用的对象。 AsInt
可让您与代表Int
的字符串进行匹配,PersonString
是Person
反序列化的实际实现。
object AsInt {
def unapply(s: String) = try{ Some(s.toInt) } catch {
case e: NumberFormatException => None
}
}
val PersonRegex = "Person\\((.*),(\\d+)\\)".r
object PersonString {
def unapply(str: String): Option[Person] = str match {
case PersonRegex(name, AsInt(age)) => Some(Person(name, age))
case _ => None
}
}
魔法在unapply
方法中,scala具有语法糖。所以使用PersonString
对象,你可以做
val person = PersonString.unapply("Person(Bob,42)")
// person will be Some(Person("Bob", 42))
或者你可以使用模式匹配块与人做事:
"Person(Bob,42)" match {
case PersonString(person) => println(person.name + " " + person.age)
case _ => println("Didn't get a person")
}
答案 1 :(得分:7)
Scala没有类型类,在这种情况下,您甚至无法使用从中继承的特征来模拟类型类,因为特征只表示对象上的方法,这意味着它们必须由“拥有” class,所以你不能把“将字符串作为唯一参数的构造函数”的定义(这是在OOP语言中可以调用的“read”)的特征。
相反,您必须自己模拟类型类。这样做(注释中的等效Haskell代码):
// class Read a where read :: String -> a
trait Read[A] { def read(s: String): A }
// instance Read Person where read = ... parser for Person ...
implicit object ReadPerson extends Read[Person] {
def read(s: String): Person = ... parser for Person ...
}
然后,当你有一个依赖于类型类的方法时,你必须将它指定为隐式上下文:
// readList :: Read a => [String] -> [a]
// readList ss = map read ss
def readList[A: Read] (ss: List[String]): List[A] = {
val r = implicitly[Read[A]] // Get the class instance of Read for type A
ss.map(r.read _)
}
用户可能会喜欢这样的多态方法,以便于使用:
object read {
def apply[A: Read](s: String): A = implicitly[Read[A]].read(s)
}
然后可以写:
val person: Person = read[Person]("Person(Bob,42)")
我不知道这个类型类的任何标准实现,特别是。
此外,免责声明:我没有Scala编译器,并且多年没有使用该语言,所以我无法保证此代码可以编译。
答案 2 :(得分:5)
这个问题的答案有点过时了。 Scala已经发现了一些新功能,特别是类型类和宏,以便更容易实现。
使用Scala Pickling library,您可以在各种序列化格式之间序列化/反序列化任意类:
import scala.pickling._
import json._
case class Person(name: String, age: Int)
val person1 = Person("Bob", 42)
val str = person1.pickle.value // { tpe: "Person", name: "Bob", age: 42 }
val person2 = JSONPickle(str).unpickle[Person]
assert(person1 == person2) // Works!
序列化器/反序列化器在编译时自动生成,因此没有反射!如果需要使用特定格式(例如案例类toString
格式)解析案例类,则可以使用自己的格式扩展此系统。
答案 3 :(得分:1)
uPickle库为此问题提供了解决方案。
答案 4 :(得分:0)
Scala使用Java的序列化内容,没有String
表示。
答案 5 :(得分:0)
从Scala 2.13
开始,可以按unapplying a string interpolator模式匹配String
s:
// case class Person(name: String, age: Int)
"Person(Bob,42)" match { case s"Person($name,$age)" => Person(name, age.toInt) }
// Person("Bob", 42)
请注意,您还可以在提取器中的{em> 中使用regex
。
在这种情况下,例如,可以帮助匹配“ Person(Bob,42)”(年龄带前导空格)并将年龄强制为整数:
val Age = "[ ?*](\\d+)".r
"Person(Bob, 42)" match {
case s"Person($name,${Age(age)})" => Some(Person(name, age.toInt))
case _ => None
}
// Person = Some(Person(Bob,42))