2012-05-29 23 views
13

我想读一个字符串作为case类的一个实例。例如,如果该功能被命名为“读取”那就让我做到以下几点:从Scala中的字符串中读取case类对象(类似于Haskell的“read”typeclass)

> case class Person(name: String, age: Int) 
> val personString: String = "Person(Bob,42)" 
> val person: Person = read(personString) 

这是相同的行为在Haskell读类型类。

+0

我在下面找到唯一的当今有用的答案,是最近描述酸洗scala库的:[scala酸洗库](http://stackoverflow.com/a/23610675/1509695)和[uPickle]( http://stackoverflow.com/a/28173416/1509695)。 – matanster

回答

12

dflemstr回答更多关于设置实际read方法 - 我会回答更多的实际解析方法。

我的方法有两个对象,可以在scala的模式匹配块中使用。 AsInt可让您匹配代表Int s的字符串,而PersonStringPerson反序列化的实际实施。

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方法,该方法具有阶语法糖。因此,使用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") 
} 
+2

我最近学到的一个很好的技巧是使用'import scala.util.control.Exception._',然后'捕获(classOf [NumberFormatException]).opt(s.toInt)'或甚至'allCatch.opt(s。 toInt)'。 –

+1

这是一个非常好的用于异常处理的小语言。我的问题是,对于这样的简单事情来说,它不会最终节省很多打字的时间,它可能只是初学者理解示例的又一步。这就是说,你得到我的+1 – Dylan

7

Scala没有类型类,在这种情况下,甚至不能使用继承的特征来模拟类型类,因为特征仅表示对象上的方法,这意味着它们必须被“拥有”一个类,所以你不能把一个“只接受一个字符串作为唯一参数的构造函数”的定义(这是在OOP语言中可能调用的“读取”)的特征。

相反,你必须自己模拟类型类。这是像这样做(在评论相当于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编译器,并且多年没有使用该语言,所以我不能保证此代码编译。

+3

我经常使用http://www.simplyscala.com/来检查像这样的Scala片段。 –

+0

我在想史密拉是否有这样的事情。 –

0

斯卡拉使用Java的序列化的东西,没有String表示。

+0

在此期间事情已经改变,请参阅DCKing的答案。 – thSoft

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格式)解析案例类,则可以使用自己的格式扩展此系统。

+0

对于以上的香草使用情况,图书馆的确反映了思考。 – matanster

相关问题